Merge "[Thread] use TestRule to filter Thread tests" into main
diff --git a/Cronet/tests/common/AndroidTest.xml b/Cronet/tests/common/AndroidTest.xml
index bded8fb..ae6b65b 100644
--- a/Cronet/tests/common/AndroidTest.xml
+++ b/Cronet/tests/common/AndroidTest.xml
@@ -35,26 +35,17 @@
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<!-- b/298380508 -->
<option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testSetLibraryLoaderIsEnforcedByDefaultEmbeddedProvider" />
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testSetLibraryLoaderIsIgnoredInNativeCronetEngineBuilderImpl" />
- <!-- b/316571753 -->
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testBaseFeatureFlagsOverridesEnabled" />
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAppliedIfAppIdMatches" />
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAreLoaded" />
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testSetLibraryLoaderIsEnforcedByDefaultEmbeddedProvider" />
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAppliedIfAtMinVersion" />
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAppliedIfAboveMinVersion" />
- <!-- b/316567693 -->
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestTest#testSSLCertificateError" />
<!-- b/316559294 -->
<option name="exclude-filter" value="org.chromium.net.NQETest#testQuicDisabled" />
<!-- b/316559294 -->
<option name="exclude-filter" value="org.chromium.net.NQETest#testPrefsWriteRead" />
<!-- b/316554711-->
- <option name="exclude-filter" value="org.chromium.net.NetworkChangesTest" />
+ <option name="exclude-filter" value="org.chromium.net.NetworkChangesTest" />
<!-- b/316550794 -->
<option name="exclude-filter" value="org.chromium.net.impl.CronetLoggerTest#testEngineCreation" />
<option name="hidden-api-checks" value="false"/>
<option name="isolated-storage" value="false"/>
+ <option name="orchestrator" value="true"/>
<option
name="device-listeners"
value="com.android.modules.utils.testing.NativeCoverageHackInstrumentationListener" />
diff --git a/Cronet/tests/mts/AndroidTest.xml b/Cronet/tests/mts/AndroidTest.xml
index bccbe29..5aed655 100644
--- a/Cronet/tests/mts/AndroidTest.xml
+++ b/Cronet/tests/mts/AndroidTest.xml
@@ -35,16 +35,6 @@
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<!-- b/298380508 -->
<option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testSetLibraryLoaderIsEnforcedByDefaultEmbeddedProvider" />
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testSetLibraryLoaderIsIgnoredInNativeCronetEngineBuilderImpl" />
- <!-- b/316571753 -->
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testBaseFeatureFlagsOverridesEnabled" />
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAppliedIfAppIdMatches" />
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAreLoaded" />
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testSetLibraryLoaderIsEnforcedByDefaultEmbeddedProvider" />
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAppliedIfAtMinVersion" />
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testHttpFlagsAppliedIfAboveMinVersion" />
- <!-- b/316567693 -->
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestTest#testSSLCertificateError" />
<!-- b/316559294 -->
<option name="exclude-filter" value="org.chromium.net.NQETest#testQuicDisabled" />
<!-- b/316559294 -->
@@ -55,6 +45,7 @@
<option name="exclude-filter" value="org.chromium.net.impl.CronetLoggerTest#testEngineCreation" />
<option name="hidden-api-checks" value="false"/>
<option name="isolated-storage" value="false"/>
+ <option name="orchestrator" value="true"/>
</test>
<!-- Only run NetHttpTests in MTS if the Tethering Mainline module is installed. -->
diff --git a/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java b/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
index 2933a44..1022b06 100644
--- a/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
+++ b/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
@@ -83,8 +83,10 @@
import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -146,6 +148,8 @@
private static final TetheringManager sTm = sContext.getSystemService(TetheringManager.class);
private static final PackageManager sPackageManager = sContext.getPackageManager();
private static final CtsNetUtils sCtsNetUtils = new CtsNetUtils(sContext);
+ private static final List<String> sCallbackErrors =
+ Collections.synchronizedList(new ArrayList<>());
// Late initialization in setUp()
private boolean mRunTests;
@@ -201,6 +205,7 @@
assumeTrue(mRunTests);
mTetheredInterfaceRequester = new TetheredInterfaceRequester();
+ sCallbackErrors.clear();
}
private boolean isEthernetTetheringSupported() throws Exception {
@@ -280,6 +285,10 @@
mHandlerThread.quitSafely();
mHandlerThread.join();
}
+
+ if (sCallbackErrors.size() > 0) {
+ fail("Some callbacks had errors: " + sCallbackErrors);
+ }
}
protected static boolean isInterfaceForTetheringAvailable() throws Exception {
@@ -391,7 +400,7 @@
}
@Override
public void onTetheredInterfacesChanged(List<String> interfaces) {
- fail("Should only call callback that takes a Set<TetheringInterface>");
+ addCallbackError("Should only call callback that takes a Set<TetheringInterface>");
}
@Override
@@ -412,7 +421,7 @@
@Override
public void onLocalOnlyInterfacesChanged(List<String> interfaces) {
- fail("Should only call callback that takes a Set<TetheringInterface>");
+ addCallbackError("Should only call callback that takes a Set<TetheringInterface>");
}
@Override
@@ -481,7 +490,7 @@
// Ignore stale callbacks registered by previous test cases.
if (mUnregistered) return;
- fail("TetheringEventCallback got error:" + error + " on iface " + ifName);
+ addCallbackError("TetheringEventCallback got error:" + error + " on iface " + ifName);
}
@Override
@@ -536,6 +545,11 @@
}
}
+ private static void addCallbackError(String error) {
+ Log.e(TAG, error);
+ sCallbackErrors.add(error);
+ }
+
protected static MyTetheringEventCallback enableEthernetTethering(String iface,
TetheringRequest request, Network expectedUpstream) throws Exception {
// Enable ethernet tethering with null expectedUpstream means the test accept any upstream
@@ -562,7 +576,7 @@
@Override
public void onTetheringFailed(int resultCode) {
- fail("Unexpectedly got onTetheringFailed");
+ addCallbackError("Unexpectedly got onTetheringFailed");
}
};
Log.d(TAG, "Starting Ethernet tethering");
diff --git a/framework/src/android/net/NetworkRequest.java b/framework/src/android/net/NetworkRequest.java
index 4de02ac..f7600b2 100644
--- a/framework/src/android/net/NetworkRequest.java
+++ b/framework/src/android/net/NetworkRequest.java
@@ -20,7 +20,6 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
@@ -41,8 +40,6 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
-// TODO : replace with android.net.flags.Flags when aconfig is supported on udc-mainline-prod
-// import android.net.NetworkCapabilities.Flags;
import android.net.NetworkCapabilities.NetCapability;
import android.net.NetworkCapabilities.Transport;
import android.os.Build;
@@ -291,18 +288,6 @@
NET_CAPABILITY_TRUSTED,
NET_CAPABILITY_VALIDATED);
- /**
- * Capabilities that are forbidden by default.
- * Forbidden capabilities only make sense in NetworkRequest, not for network agents.
- * Therefore these capabilities are only in NetworkRequest.
- */
- private static final int[] DEFAULT_FORBIDDEN_CAPABILITIES = new int[] {
- // TODO(b/313030307): this should contain NET_CAPABILITY_LOCAL_NETWORK.
- // We cannot currently add it because doing so would crash if the module rolls back,
- // because JobScheduler persists NetworkRequests to disk, and existing production code
- // does not consider LOCAL_NETWORK to be a valid capability.
- };
-
private final NetworkCapabilities mNetworkCapabilities;
// A boolean that represents whether the NOT_VCN_MANAGED capability should be deduced when
@@ -318,16 +303,6 @@
// it for apps that do not have the NETWORK_SETTINGS permission.
mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.setSingleUid(Process.myUid());
- // Default forbidden capabilities are foremost meant to help with backward
- // compatibility. When adding new types of network identified by a capability that
- // might confuse older apps, a default forbidden capability will have apps not see
- // these networks unless they explicitly ask for it.
- // If the app called clearCapabilities() it will see everything, but then it
- // can be argued that it's fair to send them too, since it asked for everything
- // explicitly.
- for (final int forbiddenCap : DEFAULT_FORBIDDEN_CAPABILITIES) {
- mNetworkCapabilities.addForbiddenCapability(forbiddenCap);
- }
}
/**
diff --git a/netbpfload/loader.cpp b/netbpfload/loader.cpp
index 3026655..013309e 100644
--- a/netbpfload/loader.cpp
+++ b/netbpfload/loader.cpp
@@ -45,10 +45,6 @@
#include "bpf/bpf_map_def.h"
#include "loader.h"
-#if BPFLOADER_VERSION < COMPILE_FOR_BPFLOADER_VERSION
-#error "BPFLOADER_VERSION is less than COMPILE_FOR_BPFLOADER_VERSION"
-#endif
-
#include <cstdlib>
#include <fstream>
#include <iostream>
@@ -781,7 +777,8 @@
.max_entries = max_entries,
.map_flags = md[i].map_flags,
};
- strlcpy(req.map_name, mapNames[i].c_str(), sizeof(req.map_name));
+ if (isAtLeastKernelVersion(4, 14, 0))
+ strlcpy(req.map_name, mapNames[i].c_str(), sizeof(req.map_name));
fd.reset(bpf(BPF_MAP_CREATE, req));
saved_errno = errno;
ALOGD("bpf_create_map name %s, ret: %d", mapNames[i].c_str(), fd.get());
@@ -1023,7 +1020,8 @@
.log_size = static_cast<__u32>(log_buf.size()),
.expected_attach_type = cs[i].expected_attach_type,
};
- strlcpy(req.prog_name, cs[i].name.c_str(), sizeof(req.prog_name));
+ if (isAtLeastKernelVersion(4, 14, 0))
+ strlcpy(req.prog_name, cs[i].name.c_str(), sizeof(req.prog_name));
fd.reset(bpf(BPF_PROG_LOAD, req));
ALOGD("BPF_PROG_LOAD call for %s (%s) returned fd: %d (%s)", elfPath,
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index 458d64f..bfcc171 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -382,10 +382,9 @@
});
}
- @VisibleForTesting(visibility = PACKAGE)
- protected void setInterfaceEnabled(@NonNull final String iface, boolean enabled,
- @Nullable final EthernetCallback cb) {
- mHandler.post(() -> updateInterfaceState(iface, enabled, cb));
+ /** Configure the administrative state of ethernet interface by toggling IFF_UP. */
+ public void setInterfaceEnabled(String iface, boolean enabled, EthernetCallback cb) {
+ mHandler.post(() -> setInterfaceAdministrativeState(iface, enabled, cb));
}
IpConfiguration getIpConfiguration(String iface) {
@@ -643,25 +642,40 @@
}
}
- private void updateInterfaceState(String iface, boolean up) {
- updateInterfaceState(iface, up, new EthernetCallback(null /* cb */));
- }
-
- // TODO(b/225315248): enable/disableInterface() should not affect link state.
- private void updateInterfaceState(String iface, boolean up, EthernetCallback cb) {
- final int mode = getInterfaceMode(iface);
- if (mode == INTERFACE_MODE_SERVER || !mFactory.hasInterface(iface)) {
- // The interface is in server mode or is not tracked.
- cb.onError("Failed to set link state " + (up ? "up" : "down") + " for " + iface);
+ private void setInterfaceAdministrativeState(String iface, boolean up, EthernetCallback cb) {
+ if (getInterfaceState(iface) == EthernetManager.STATE_ABSENT) {
+ cb.onError("Failed to enable/disable absent interface: " + iface);
+ return;
+ }
+ if (getInterfaceRole(iface) == EthernetManager.ROLE_SERVER) {
+ // TODO: support setEthernetState for server mode interfaces.
+ cb.onError("Failed to enable/disable interface in server mode: " + iface);
return;
}
+ if (up) {
+ // WARNING! setInterfaceUp() clears the IPv4 address and readds it. Calling
+ // enableInterface() on an active interface can lead to a provisioning failure which
+ // will cause IpClient to be restarted.
+ // TODO: use netlink directly rather than calling into netd.
+ NetdUtils.setInterfaceUp(mNetd, iface);
+ } else {
+ NetdUtils.setInterfaceDown(mNetd, iface);
+ }
+ cb.onResult(iface);
+ }
+
+ private void updateInterfaceState(String iface, boolean up) {
+ final int mode = getInterfaceMode(iface);
+ if (mode == INTERFACE_MODE_SERVER) {
+ // TODO: support tracking link state for interfaces in server mode.
+ return;
+ }
+
+ // If updateInterfaceLinkState returns false, the interface is already in the correct state.
if (mFactory.updateInterfaceLinkState(iface, up)) {
broadcastInterfaceStateChange(iface);
}
- // If updateInterfaceLinkState returns false, the interface is already in the correct state.
- // Always return success.
- cb.onResult(iface);
}
private void maybeUpdateServerModeInterfaceState(String iface, boolean available) {
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 8f09a40..f27e645 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -117,6 +117,9 @@
import static com.android.net.module.util.PermissionUtils.hasAnyPermissionOf;
import static com.android.server.ConnectivityStatsLog.CONNECTIVITY_STATE_SAMPLE;
import static com.android.server.connectivity.ConnectivityFlags.REQUEST_RESTRICTED_WIFI;
+import static com.android.server.connectivity.ConnectivityFlags.INGRESS_TO_VPN_ADDRESS_FILTERING;
+
+import static java.util.Map.Entry;
import android.Manifest;
import android.annotation.CheckResult;
@@ -1002,6 +1005,9 @@
// Uids that ConnectivityService is pending to close sockets of.
private final Set<Integer> mPendingFrozenUids = new ArraySet<>();
+ // Flag to drop packets to VPN addresses ingressing via non-VPN interfaces.
+ private final boolean mIngressToVpnAddressFiltering;
+
/**
* Implements support for the legacy "one network per network type" model.
*
@@ -1979,6 +1985,8 @@
activityManager.registerUidFrozenStateChangedCallback(
(Runnable r) -> r.run(), frozenStateChangedCallback);
}
+ mIngressToVpnAddressFiltering = mDeps.isAtLeastT()
+ && mDeps.isFeatureNotChickenedOut(mContext, INGRESS_TO_VPN_ADDRESS_FILTERING);
}
/**
@@ -2766,6 +2774,7 @@
private boolean canSeeAllowedUids(final int pid, final int uid, final int netOwnerUid) {
return Process.SYSTEM_UID == uid
+ || netOwnerUid == uid
|| hasAnyPermissionOf(mContext, pid, uid,
android.Manifest.permission.NETWORK_FACTORY);
}
@@ -2793,7 +2802,6 @@
}
if (!canSeeAllowedUids(callerPid, callerUid, newNc.getOwnerUid())) {
newNc.setAllowedUids(new ArraySet<>());
- newNc.setSubscriptionIds(Collections.emptySet());
}
redactUnderlyingNetworksForCapabilities(newNc, callerPid, callerUid);
@@ -5344,6 +5352,7 @@
// was is being disconnected the callbacks have already been sent, and if it is being
// destroyed pending replacement they will be sent when it is disconnected.
maybeDisableForwardRulesForDisconnectingNai(nai, false /* sendCallbacks */);
+ updateIngressToVpnAddressFiltering(null, nai.linkProperties, nai);
try {
mNetd.networkDestroy(nai.network.getNetId());
} catch (RemoteException | ServiceSpecificException e) {
@@ -6021,7 +6030,15 @@
if (nm == null) return;
if (request == CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED) {
- enforceNetworkStackPermission(mContext);
+ // This enforceNetworkStackPermission() should be adopted to check
+ // the required permission but this may be break OEM captive portal
+ // apps. Simply ignore the request if the caller does not have
+ // permission.
+ if (!hasNetworkStackPermission()) {
+ Log.e(TAG, "Calling appRequest() without proper permission. Skip");
+ return;
+ }
+
nm.forceReevaluation(mDeps.getCallingUid());
}
}
@@ -7584,15 +7601,6 @@
"Insufficient permissions to request a specific signal strength");
}
mAppOpsManager.checkPackage(callerUid, callerPackageName);
-
- if (nc.getSubscriptionIds().isEmpty()) {
- return;
- }
- if (mRequestRestrictedWifiEnabled
- && canRequestRestrictedNetworkDueToCarrierPrivileges(nc, callerUid)) {
- return;
- }
- enforceNetworkFactoryPermission();
}
private int[] getSignalStrengthThresholds(@NonNull final NetworkAgentInfo nai) {
@@ -8695,6 +8703,8 @@
// new interface (the interface name -> index map becomes initialized)
updateVpnFiltering(newLp, oldLp, networkAgent);
+ updateIngressToVpnAddressFiltering(newLp, oldLp, networkAgent);
+
updateMtu(newLp, oldLp);
// TODO - figure out what to do for clat
// for (LinkProperties lp : newLp.getStackedLinks()) {
@@ -8986,6 +8996,87 @@
}
}
+ /**
+ * Returns ingress discard rules to drop packets to VPN addresses ingressing via non-VPN
+ * interfaces.
+ * Ingress discard rule is added to the address iff
+ * 1. The address is not a link local address
+ * 2. The address is used by a single VPN interface and not used by any other
+ * interfaces even non-VPN ones
+ * This method can be called during network disconnects, when nai has already been removed from
+ * mNetworkAgentInfos.
+ *
+ * @param nai This method generates rules assuming lp of this nai is the lp at the second
+ * argument.
+ * @param lp This method generates rules assuming lp of nai at the first argument is this lp.
+ * Caller passes old lp to generate old rules and new lp to generate new rules.
+ * @return ingress discard rules. Set of pairs of addresses and interface names
+ */
+ private Set<Pair<InetAddress, String>> generateIngressDiscardRules(
+ @NonNull final NetworkAgentInfo nai, @Nullable final LinkProperties lp) {
+ Set<NetworkAgentInfo> nais = new ArraySet<>(mNetworkAgentInfos);
+ nais.add(nai);
+ // Determine how many networks each IP address is currently configured on.
+ // Ingress rules are added only for IP addresses that are configured on single interface.
+ final Map<InetAddress, Integer> addressOwnerCounts = new ArrayMap<>();
+ for (final NetworkAgentInfo agent : nais) {
+ if (agent.isDestroyed()) {
+ continue;
+ }
+ final LinkProperties agentLp = (nai == agent) ? lp : agent.linkProperties;
+ if (agentLp == null) {
+ continue;
+ }
+ for (final InetAddress addr: agentLp.getAllAddresses()) {
+ addressOwnerCounts.put(addr, addressOwnerCounts.getOrDefault(addr, 0) + 1);
+ }
+ }
+
+ // Iterates all networks instead of only generating rule for nai that was passed in since
+ // lp of the nai change could cause/resolve address collision and result in affecting rule
+ // for different network.
+ final Set<Pair<InetAddress, String>> ingressDiscardRules = new ArraySet<>();
+ for (final NetworkAgentInfo agent : nais) {
+ if (!agent.isVPN() || agent.isDestroyed()) {
+ continue;
+ }
+ final LinkProperties agentLp = (nai == agent) ? lp : agent.linkProperties;
+ if (agentLp == null || agentLp.getInterfaceName() == null) {
+ continue;
+ }
+
+ for (final InetAddress addr: agentLp.getAllAddresses()) {
+ if (addressOwnerCounts.get(addr) == 1 && !addr.isLinkLocalAddress()) {
+ ingressDiscardRules.add(new Pair<>(addr, agentLp.getInterfaceName()));
+ }
+ }
+ }
+ return ingressDiscardRules;
+ }
+
+ private void updateIngressToVpnAddressFiltering(@Nullable LinkProperties newLp,
+ @Nullable LinkProperties oldLp, @NonNull NetworkAgentInfo nai) {
+ // Having isAtleastT to avoid NewApi linter error (b/303382209)
+ if (!mIngressToVpnAddressFiltering || !mDeps.isAtLeastT()) {
+ return;
+ }
+ final CompareOrUpdateResult<InetAddress, Pair<InetAddress, String>> ruleDiff =
+ new CompareOrUpdateResult<>(
+ generateIngressDiscardRules(nai, oldLp),
+ generateIngressDiscardRules(nai, newLp),
+ (rule) -> rule.first);
+ for (Pair<InetAddress, String> rule: ruleDiff.removed) {
+ mBpfNetMaps.removeIngressDiscardRule(rule.first);
+ }
+ for (Pair<InetAddress, String> rule: ruleDiff.added) {
+ mBpfNetMaps.setIngressDiscardRule(rule.first, rule.second);
+ }
+ // setIngressDiscardRule overrides the existing rule
+ for (Pair<InetAddress, String> rule: ruleDiff.updated) {
+ mBpfNetMaps.setIngressDiscardRule(rule.first, rule.second);
+ }
+ }
+
private void updateWakeOnLan(@NonNull LinkProperties lp) {
if (mWolSupportedInterfaces == null) {
mWolSupportedInterfaces = new ArraySet<>(mResources.get().getStringArray(
@@ -9176,7 +9267,7 @@
// 3. The app doesn't have Carrier Privileges
// 4. The app doesn't have permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS
for (final NetworkRequest nr : mNetworkRequests.keySet()) {
- if ((nr.isRequest() || nr.isListen())
+ if (nr.isRequest()
&& !nr.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
&& nr.getRequestorUid() == uid
&& getSubscriptionIdFromNetworkCaps(nr.networkCapabilities) == subId
diff --git a/service/src/com/android/server/connectivity/ConnectivityFlags.java b/service/src/com/android/server/connectivity/ConnectivityFlags.java
index bf09160..a55c683 100644
--- a/service/src/com/android/server/connectivity/ConnectivityFlags.java
+++ b/service/src/com/android/server/connectivity/ConnectivityFlags.java
@@ -38,6 +38,10 @@
public static final String REQUEST_RESTRICTED_WIFI =
"request_restricted_wifi";
+
+ public static final String INGRESS_TO_VPN_ADDRESS_FILTERING =
+ "ingress_to_vpn_address_filtering";
+
private boolean mNoRematchAllRequestsOnRegister;
/**
diff --git a/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h b/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
index ac922cd..92ddf44 100644
--- a/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
+++ b/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
@@ -39,7 +39,13 @@
// Android U / 14 (api level 34) - various new program types added
#define BPFLOADER_U_VERSION 37u
-// Android V / 15 (api level 35) - this bpfloader should eventually go back to T
+// Android V / 15 (api level 35) - platform only
+// (note: the platform bpfloader in V isn't really versioned at all,
+// as there is no need as it can only load objects compiled at the
+// same time as itself and the rest of the platform)
+#define BPFLOADER_V_VERSION 41u
+
+// Android Mainline - this bpfloader should eventually go back to T
#define BPFLOADER_MAINLINE_VERSION 42u
/* For mainline module use, you can #define BPFLOADER_{MIN/MAX}_VER
@@ -51,7 +57,7 @@
* In which case it's just best to use the default.
*/
#ifndef BPFLOADER_MIN_VER
-#define BPFLOADER_MIN_VER COMPILE_FOR_BPFLOADER_VERSION
+#define BPFLOADER_MIN_VER BPFLOADER_V_VERSION
#endif
#ifndef BPFLOADER_MAX_VER
diff --git a/staticlibs/native/bpf_headers/include/bpf/bpf_map_def.h b/staticlibs/native/bpf_headers/include/bpf/bpf_map_def.h
index ef03c4d..00ef91a 100644
--- a/staticlibs/native/bpf_headers/include/bpf/bpf_map_def.h
+++ b/staticlibs/native/bpf_headers/include/bpf/bpf_map_def.h
@@ -48,9 +48,6 @@
#define DEFAULT_SIZEOF_BPF_MAP_DEF 32 // v0.0 struct: enum (uint sized) + 7 uint
#define DEFAULT_SIZEOF_BPF_PROG_DEF 20 // v0.0 struct: 4 uint + bool + 3 byte alignment pad
-// By default, unless otherwise specified, allow the use of features only supported by v0.37.
-#define COMPILE_FOR_BPFLOADER_VERSION 37u
-
/*
* The bpf_{map,prog}_def structures are compiled for different architectures.
* Once by the BPF compiler for the BPF architecture, and once by a C++
diff --git a/staticlibs/testutils/hostdevice/com/android/testutils/ConcurrentUtils.kt b/staticlibs/testutils/hostdevice/com/android/testutils/ConcurrentUtils.kt
index af4f96d..c6e5f25 100644
--- a/staticlibs/testutils/hostdevice/com/android/testutils/ConcurrentUtils.kt
+++ b/staticlibs/testutils/hostdevice/com/android/testutils/ConcurrentUtils.kt
@@ -19,10 +19,77 @@
package com.android.testutils
import java.util.concurrent.CountDownLatch
+import java.util.concurrent.ExecutorService
import java.util.concurrent.TimeUnit
+import java.util.function.Consumer
import kotlin.system.measureTimeMillis
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
// For Java usage
fun durationOf(fn: Runnable) = measureTimeMillis { fn.run() }
fun CountDownLatch.await(timeoutMs: Long): Boolean = await(timeoutMs, TimeUnit.MILLISECONDS)
+
+/**
+ * Quit resources provided as a list by a supplier.
+ *
+ * The supplier may return more resources as the process progresses, for example while interrupting
+ * threads and waiting for them to finish they may spawn more threads, so this implements a
+ * [maxRetryCount] which, in this case, would be the maximum length of the thread chain that can be
+ * terminated.
+ */
+fun <T> quitResources(
+ maxRetryCount: Int,
+ supplier: () -> List<T>,
+ terminator: Consumer<T>
+) {
+ // Run it multiple times since new threads might be generated in a thread
+ // that is about to be terminated
+ for (retryCount in 0 until maxRetryCount) {
+ val resourcesToBeCleared = supplier()
+ if (resourcesToBeCleared.isEmpty()) return
+ for (resource in resourcesToBeCleared) {
+ terminator.accept(resource)
+ }
+ }
+ assertEmpty(supplier())
+}
+
+/**
+ * Implementation of [quitResources] to interrupt and wait for [ExecutorService]s to finish.
+ */
+@JvmOverloads
+fun quitExecutorServices(
+ maxRetryCount: Int,
+ interrupt: Boolean = true,
+ timeoutMs: Long = 10_000L,
+ supplier: () -> List<ExecutorService>
+) {
+ quitResources(maxRetryCount, supplier) { ecs ->
+ if (interrupt) {
+ ecs.shutdownNow()
+ }
+ assertTrue(ecs.awaitTermination(timeoutMs, TimeUnit.MILLISECONDS),
+ "ExecutorServices did not terminate within timeout")
+ }
+}
+
+/**
+ * Implementation of [quitResources] to interrupt and wait for [Thread]s to finish.
+ */
+@JvmOverloads
+fun quitThreads(
+ maxRetryCount: Int,
+ interrupt: Boolean = true,
+ timeoutMs: Long = 10_000L,
+ supplier: () -> List<Thread>
+) {
+ quitResources(maxRetryCount, supplier) { th ->
+ if (interrupt) {
+ th.interrupt()
+ }
+ th.join(timeoutMs)
+ assertFalse(th.isAlive, "Threads did not terminate within timeout.")
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 1241e18..2ca8832 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -160,10 +160,6 @@
private static final long BROADCAST_TIMEOUT_MS = 5_000;
- // Should be kept in sync with the constant in NetworkPolicyManagerService.
- // TODO: b/322115994 - remove once the feature is in staging.
- private static final boolean ALWAYS_RESTRICT_BACKGROUND_NETWORK = false;
-
protected Context mContext;
protected Instrumentation mInstrumentation;
protected ConnectivityManager mCm;
@@ -233,8 +229,9 @@
}
final String output = executeShellCommand("device_config get backstage_power"
+ " com.android.server.net.network_blocked_for_top_sleeping_and_above");
- return Boolean.parseBoolean(output) && ALWAYS_RESTRICT_BACKGROUND_NETWORK;
+ return Boolean.parseBoolean(output);
}
+
protected int getUid(String packageName) throws Exception {
return mContext.getPackageManager().getPackageUid(packageName, 0);
}
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 df2e7a6..f81a03d 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
@@ -24,6 +24,7 @@
import static android.content.pm.PackageManager.FEATURE_WIFI;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
@@ -72,6 +73,7 @@
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
+import android.net.InetAddresses;
import android.net.IpSecManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -91,6 +93,7 @@
import android.net.cts.util.CtsNetUtils;
import android.net.util.KeepaliveUtils;
import android.net.wifi.WifiManager;
+import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -249,6 +252,7 @@
@Before
public void setUp() throws Exception {
+ assumeTrue(supportedHardware());
mNetwork = null;
mTestContext = getInstrumentation().getContext();
mTargetContext = getInstrumentation().getTargetContext();
@@ -879,7 +883,6 @@
@Test @IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
public void testChangeUnderlyingNetworks() throws Exception {
- assumeTrue(supportedHardware());
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
final TestableNetworkCallback callback = new TestableNetworkCallback();
@@ -938,7 +941,6 @@
@Test
public void testDefault() throws Exception {
- assumeTrue(supportedHardware());
if (!SdkLevel.isAtLeastS() && (
SystemProperties.getInt("persist.adb.tcp.port", -1) > -1
|| SystemProperties.getInt("service.adb.tcp.port", -1) > -1)) {
@@ -1033,8 +1035,6 @@
@Test
public void testAppAllowed() throws Exception {
- assumeTrue(supportedHardware());
-
FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
// Shell app must not be put in here or it would kill the ADB-over-network use case
@@ -1142,8 +1142,6 @@
}
private void doTestAutomaticOnOffKeepaliveMode(final boolean closeSocket) throws Exception {
- assumeTrue(supportedHardware());
-
// Get default network first before starting VPN
final Network defaultNetwork = mCM.getActiveNetwork();
final TestableNetworkCallback cb = new TestableNetworkCallback();
@@ -1231,8 +1229,6 @@
@Test
public void testAppDisallowed() throws Exception {
- assumeTrue(supportedHardware());
-
FileDescriptor localFd = openSocketFd(TEST_HOST, 80, TIMEOUT_MS);
FileDescriptor remoteFd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
@@ -1265,8 +1261,6 @@
@Test
public void testSocketClosed() throws Exception {
- assumeTrue(supportedHardware());
-
final FileDescriptor localFd = openSocketFd(TEST_HOST, 80, TIMEOUT_MS);
final List<FileDescriptor> remoteFds = new ArrayList<>();
@@ -1290,7 +1284,6 @@
@Test
public void testExcludedRoutes() throws Exception {
- assumeTrue(supportedHardware());
assumeTrue(SdkLevel.isAtLeastT());
// Shell app must not be put in here or it would kill the ADB-over-network use case
@@ -1311,8 +1304,6 @@
@Test
public void testIncludedRoutes() throws Exception {
- assumeTrue(supportedHardware());
-
// Shell app must not be put in here or it would kill the ADB-over-network use case
String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */,
@@ -1330,7 +1321,6 @@
@Test
public void testInterleavedRoutes() throws Exception {
- assumeTrue(supportedHardware());
assumeTrue(SdkLevel.isAtLeastT());
// Shell app must not be put in here or it would kill the ADB-over-network use case
@@ -1358,8 +1348,6 @@
@Test
public void testGetConnectionOwnerUidSecurity() throws Exception {
- assumeTrue(supportedHardware());
-
DatagramSocket s;
InetAddress address = InetAddress.getByName("localhost");
s = new DatagramSocket();
@@ -1380,7 +1368,6 @@
@Test
public void testSetProxy() throws Exception {
- assumeTrue(supportedHardware());
ProxyInfo initialProxy = mCM.getDefaultProxy();
// Receiver for the proxy change broadcast.
BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
@@ -1420,7 +1407,6 @@
@Test
public void testSetProxyDisallowedApps() throws Exception {
- assumeTrue(supportedHardware());
ProxyInfo initialProxy = mCM.getDefaultProxy();
String disallowedApps = mPackageName;
@@ -1446,7 +1432,6 @@
@Test
public void testNoProxy() throws Exception {
- assumeTrue(supportedHardware());
ProxyInfo initialProxy = mCM.getDefaultProxy();
BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
proxyBroadcastReceiver.register();
@@ -1481,7 +1466,6 @@
@Test
public void testBindToNetworkWithProxy() throws Exception {
- assumeTrue(supportedHardware());
String allowedApps = mPackageName;
Network initialNetwork = mCM.getActiveNetwork();
ProxyInfo initialProxy = mCM.getDefaultProxy();
@@ -1506,9 +1490,6 @@
@Test
public void testVpnMeterednessWithNoUnderlyingNetwork() throws Exception {
- if (!supportedHardware()) {
- return;
- }
// VPN is not routing any traffic i.e. its underlying networks is an empty array.
ArrayList<Network> underlyingNetworks = new ArrayList<>();
String allowedApps = mPackageName;
@@ -1538,9 +1519,6 @@
@Test
public void testVpnMeterednessWithNullUnderlyingNetwork() throws Exception {
- if (!supportedHardware()) {
- return;
- }
Network underlyingNetwork = mCM.getActiveNetwork();
if (underlyingNetwork == null) {
Log.i(TAG, "testVpnMeterednessWithNullUnderlyingNetwork cannot execute"
@@ -1567,9 +1545,6 @@
@Test
public void testVpnMeterednessWithNonNullUnderlyingNetwork() throws Exception {
- if (!supportedHardware()) {
- return;
- }
Network underlyingNetwork = mCM.getActiveNetwork();
if (underlyingNetwork == null) {
Log.i(TAG, "testVpnMeterednessWithNonNullUnderlyingNetwork cannot execute"
@@ -1609,9 +1584,6 @@
@Test
public void testAlwaysMeteredVpnWithNullUnderlyingNetwork() throws Exception {
- if (!supportedHardware()) {
- return;
- }
Network underlyingNetwork = mCM.getActiveNetwork();
if (underlyingNetwork == null) {
Log.i(TAG, "testAlwaysMeteredVpnWithNullUnderlyingNetwork cannot execute"
@@ -1636,9 +1608,6 @@
@Test
public void testAlwaysMeteredVpnWithNonNullUnderlyingNetwork() throws Exception {
- if (!supportedHardware()) {
- return;
- }
Network underlyingNetwork = mCM.getActiveNetwork();
if (underlyingNetwork == null) {
Log.i(TAG, "testAlwaysMeteredVpnWithNonNullUnderlyingNetwork cannot execute"
@@ -1676,9 +1645,6 @@
@Test
public void testB141603906() throws Exception {
- if (!supportedHardware()) {
- return;
- }
final InetSocketAddress src = new InetSocketAddress(0);
final InetSocketAddress dst = new InetSocketAddress(0);
final int NUM_THREADS = 8;
@@ -1786,8 +1752,6 @@
*/
@Test
public void testDownloadWithDownloadManagerDisallowed() throws Exception {
- assumeTrue(supportedHardware());
-
// Start a VPN with DownloadManager package in disallowed list.
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
new String[] {"192.0.2.0/24", "2001:db8::/32"},
@@ -1843,7 +1807,6 @@
@Test @IgnoreUpTo(Build.VERSION_CODES.R)
public void testBlockIncomingPackets() throws Exception {
- assumeTrue(supportedHardware());
final Network network = mCM.getActiveNetwork();
assertNotNull("Requires a working Internet connection", network);
@@ -1912,7 +1875,6 @@
@Test
public void testSetVpnDefaultForUids() throws Exception {
- assumeTrue(supportedHardware());
assumeTrue(SdkLevel.isAtLeastU());
final Network defaultNetwork = mCM.getActiveNetwork();
@@ -1958,6 +1920,81 @@
});
}
+ /**
+ * Check if packets to a VPN interface's IP arriving on a non-VPN interface are dropped or not.
+ * If the test interface has a different address from the VPN interface, packets must be dropped
+ * If the test interface has the same address as the VPN interface, packets must not be
+ * dropped
+ *
+ * @param duplicatedAddress true to bring up the test interface with the same address as the VPN
+ * interface
+ */
+ private void doTestDropPacketToVpnAddress(final boolean duplicatedAddress)
+ throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .addTransportType(TRANSPORT_TEST)
+ .build();
+ final CtsNetUtils.TestNetworkCallback callback = new CtsNetUtils.TestNetworkCallback();
+ mCM.requestNetwork(request, callback);
+ final FileDescriptor srcTunFd = runWithShellPermissionIdentity(() -> {
+ final TestNetworkManager tnm = mTestContext.getSystemService(TestNetworkManager.class);
+ List<LinkAddress> linkAddresses = duplicatedAddress
+ ? List.of(new LinkAddress("192.0.2.2/24"),
+ new LinkAddress("2001:db8:1:2::ffe/64")) :
+ List.of(new LinkAddress("198.51.100.2/24"),
+ new LinkAddress("2001:db8:3:4::ffe/64"));
+ final TestNetworkInterface iface = tnm.createTunInterface(linkAddresses);
+ tnm.setupTestNetwork(iface.getInterfaceName(), new Binder());
+ return iface.getFileDescriptor().getFileDescriptor();
+ }, MANAGE_TEST_NETWORKS);
+ final Network testNetwork = callback.waitForAvailable();
+ assertNotNull(testNetwork);
+ final DatagramSocket dstSock = new DatagramSocket();
+
+ testAndCleanup(() -> {
+ startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */,
+ new String[]{"0.0.0.0/0", "::/0"} /* routes */,
+ "" /* allowedApplications */, "" /* disallowedApplications */,
+ null /* proxyInfo */, null /* underlyingNetworks */,
+ false /* isAlwaysMetered */);
+
+ final FileDescriptor dstUdpFd = dstSock.getFileDescriptor$();
+ checkBlockUdp(srcTunFd, dstUdpFd,
+ InetAddresses.parseNumericAddress("192.0.2.2") /* dstAddress */,
+ InetAddresses.parseNumericAddress("192.0.2.1") /* srcAddress */,
+ duplicatedAddress ? EXPECT_PASS : EXPECT_BLOCK);
+ checkBlockUdp(srcTunFd, dstUdpFd,
+ InetAddresses.parseNumericAddress("2001:db8:1:2::ffe") /* dstAddress */,
+ InetAddresses.parseNumericAddress("2001:db8:1:2::ffa") /* srcAddress */,
+ duplicatedAddress ? EXPECT_PASS : EXPECT_BLOCK);
+
+ // Traffic on VPN should not be affected
+ checkTrafficOnVpn();
+ }, /* cleanup */ () -> {
+ Os.close(srcTunFd);
+ dstSock.close();
+ }, /* cleanup */ () -> {
+ runWithShellPermissionIdentity(() -> {
+ mTestContext.getSystemService(TestNetworkManager.class)
+ .teardownTestNetwork(testNetwork);
+ }, MANAGE_TEST_NETWORKS);
+ }, /* cleanup */ () -> {
+ mCM.unregisterNetworkCallback(callback);
+ });
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testDropPacketToVpnAddress_WithoutDuplicatedAddress() throws Exception {
+ doTestDropPacketToVpnAddress(false /* duplicatedAddress */);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testDropPacketToVpnAddress_WithDuplicatedAddress() throws Exception {
+ doTestDropPacketToVpnAddress(true /* duplicatedAddress */);
+ }
+
private ByteBuffer buildIpv4UdpPacket(final Inet4Address dstAddr, final Inet4Address srcAddr,
final short dstPort, final short srcPort, final byte[] payload) throws IOException {
@@ -2001,7 +2038,8 @@
private void checkBlockUdp(
final FileDescriptor srcTunFd,
final FileDescriptor dstUdpFd,
- final boolean ipv6,
+ final InetAddress dstAddress,
+ final InetAddress srcAddress,
final boolean expectBlock) throws Exception {
final Random random = new Random();
final byte[] sendData = new byte[100];
@@ -2009,15 +2047,15 @@
final short dstPort = (short) ((InetSocketAddress) Os.getsockname(dstUdpFd)).getPort();
ByteBuffer buf;
- if (ipv6) {
+ if (dstAddress instanceof Inet6Address) {
buf = buildIpv6UdpPacket(
- (Inet6Address) TEST_IP6_DST_ADDR.getAddress(),
- (Inet6Address) TEST_IP6_SRC_ADDR.getAddress(),
+ (Inet6Address) dstAddress,
+ (Inet6Address) srcAddress,
dstPort, TEST_SRC_PORT, sendData);
} else {
buf = buildIpv4UdpPacket(
- (Inet4Address) TEST_IP4_DST_ADDR.getAddress(),
- (Inet4Address) TEST_IP4_SRC_ADDR.getAddress(),
+ (Inet4Address) dstAddress,
+ (Inet4Address) srcAddress,
dstPort, TEST_SRC_PORT, sendData);
}
@@ -2043,8 +2081,10 @@
final FileDescriptor srcTunFd,
final FileDescriptor dstUdpFd,
final boolean expectBlock) throws Exception {
- checkBlockUdp(srcTunFd, dstUdpFd, false /* ipv6 */, expectBlock);
- checkBlockUdp(srcTunFd, dstUdpFd, true /* ipv6 */, expectBlock);
+ checkBlockUdp(srcTunFd, dstUdpFd, TEST_IP4_DST_ADDR.getAddress(),
+ TEST_IP4_SRC_ADDR.getAddress(), expectBlock);
+ checkBlockUdp(srcTunFd, dstUdpFd, TEST_IP6_DST_ADDR.getAddress(),
+ TEST_IP6_SRC_ADDR.getAddress(), expectBlock);
}
private class DetailedBlockedStatusCallback extends TestableNetworkCallback {
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
index 4f21af7..f0a87af 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
@@ -171,4 +171,16 @@
public void testSetVpnDefaultForUids() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testSetVpnDefaultForUids");
}
+
+ @Test
+ public void testDropPacketToVpnAddress_WithoutDuplicatedAddress() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest",
+ "testDropPacketToVpnAddress_WithoutDuplicatedAddress");
+ }
+
+ @Test
+ public void testDropPacketToVpnAddress_WithDuplicatedAddress() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest",
+ "testDropPacketToVpnAddress_WithDuplicatedAddress");
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index b7e5205..7af3c83 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -899,6 +899,20 @@
}
@Test
+ fun testEnableDisableInterface_callbacks() {
+ val iface = createInterface()
+ val listener = EthernetStateListener()
+ addInterfaceStateListener(listener)
+ listener.expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT)
+
+ disableInterface(iface).expectResult(iface.name)
+ listener.expectCallback(iface, STATE_LINK_DOWN, ROLE_CLIENT)
+
+ enableInterface(iface).expectResult(iface.name)
+ listener.expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT)
+ }
+
+ @Test
fun testUpdateConfiguration_forBothIpConfigAndCapabilities() {
val iface = createInterface()
val cb = requestNetwork(ETH_REQUEST.copyWithEthernetSpecifier(iface.name))
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 8dbcf2f..0daf7fe 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -2199,15 +2199,10 @@
}
private fun hasServiceTypeClientsForNetwork(clients: List<String>, network: Network): Boolean {
- for (client in clients) {
- val netid = client.substring(
- client.indexOf("network=") + "network=".length,
- client.indexOf("interfaceIndex=") - 1)
- if (netid == network.toString()) {
- return true
- }
+ return clients.any { client -> client.substring(
+ client.indexOf("network=") + "network=".length,
+ client.indexOf("interfaceIndex=") - 1) == network.getNetId().toString()
}
- return false
}
/**
diff --git a/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt b/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
index 104d063..3d948ba 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
@@ -18,10 +18,14 @@
import android.app.Service
import android.content.Intent
+import androidx.annotation.GuardedBy
+import com.android.testutils.quitExecutorServices
+import com.android.testutils.quitThreads
import java.net.URL
import java.util.Collections
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentLinkedQueue
+import java.util.concurrent.ExecutorService
import kotlin.collections.ArrayList
import kotlin.test.fail
@@ -37,7 +41,12 @@
.run {
withDefault { key -> getOrPut(key) { ConcurrentLinkedQueue() } }
}
- private val httpRequestUrls = Collections.synchronizedList(ArrayList<String>())
+ private val httpRequestUrls = Collections.synchronizedList(mutableListOf<String>())
+
+ @GuardedBy("networkMonitorThreads")
+ private val networkMonitorThreads = mutableListOf<Thread>()
+ @GuardedBy("networkMonitorExecutorServices")
+ private val networkMonitorExecutorServices = mutableListOf<ExecutorService>()
/**
* Called when an HTTP request is being processed by NetworkMonitor. Returns the response
@@ -52,10 +61,47 @@
}
/**
+ * Called when NetworkMonitor creates a new Thread.
+ */
+ fun onNetworkMonitorThreadCreated(thread: Thread) {
+ synchronized(networkMonitorThreads) {
+ networkMonitorThreads.add(thread)
+ }
+ }
+
+ /**
+ * Called when NetworkMonitor creates a new ExecutorService.
+ */
+ fun onNetworkMonitorExecutorServiceCreated(executorService: ExecutorService) {
+ synchronized(networkMonitorExecutorServices) {
+ networkMonitorExecutorServices.add(executorService)
+ }
+ }
+
+ /**
* Clear all state of this connector. This is intended for use between two tests, so all
* state should be reset as if the connector was just created.
*/
override fun clearAllState() {
+ quitThreads(
+ maxRetryCount = 3,
+ interrupt = true) {
+ synchronized(networkMonitorThreads) {
+ networkMonitorThreads.toList().also { networkMonitorThreads.clear() }
+ }
+ }
+ quitExecutorServices(
+ maxRetryCount = 3,
+ // NetworkMonitor is expected to have interrupted its executors when probing
+ // finishes, otherwise it's a thread pool leak that should be caught, so they should
+ // not need to be interrupted (the test only needs to wait for them to finish).
+ interrupt = false) {
+ synchronized(networkMonitorExecutorServices) {
+ networkMonitorExecutorServices.toList().also {
+ networkMonitorExecutorServices.clear()
+ }
+ }
+ }
httpResponses.clear()
httpRequestUrls.clear()
}
diff --git a/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
index 7e227c4..e43ce29 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
@@ -30,13 +30,14 @@
import com.android.server.NetworkStackService.NetworkStackConnector
import com.android.server.connectivity.NetworkMonitor
import com.android.server.net.integrationtests.NetworkStackInstrumentationService.InstrumentationConnector
-import org.mockito.Mockito.doReturn
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.spy
import java.io.ByteArrayInputStream
import java.net.HttpURLConnection
import java.net.URL
import java.nio.charset.StandardCharsets
+import java.util.concurrent.ExecutorService
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
private const val TEST_NETID = 42
@@ -60,6 +61,10 @@
private class NetworkMonitorDeps(private val privateDnsBypassNetwork: Network) :
NetworkMonitor.Dependencies() {
override fun getPrivateDnsBypassNetwork(network: Network?) = privateDnsBypassNetwork
+ override fun onThreadCreated(thread: Thread) =
+ InstrumentationConnector.onNetworkMonitorThreadCreated(thread)
+ override fun onExecutorServiceCreated(ecs: ExecutorService) =
+ InstrumentationConnector.onNetworkMonitorExecutorServiceCreated(ecs)
}
/**
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 8f768b2..f5eee42 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -172,6 +172,7 @@
import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType;
import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackRegister;
import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister;
+import static com.android.server.connectivity.ConnectivityFlags.INGRESS_TO_VPN_ADDRESS_FILTERING;
import static com.android.testutils.Cleanup.testAndCleanup;
import static com.android.testutils.ConcurrentUtils.await;
import static com.android.testutils.ConcurrentUtils.durationOf;
@@ -2179,6 +2180,8 @@
return true;
case ALLOW_SATALLITE_NETWORK_FALLBACK:
return true;
+ case INGRESS_TO_VPN_ADDRESS_FILTERING:
+ return true;
default:
return super.isFeatureNotChickenedOut(context, name);
}
@@ -17338,21 +17341,7 @@
}
@Test
- public void testSubIdsClearedWithoutNetworkFactoryPermission() throws Exception {
- mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED);
- final NetworkCapabilities nc = new NetworkCapabilities();
- nc.setSubscriptionIds(Collections.singleton(Process.myUid()));
-
- final NetworkCapabilities result =
- mService.networkCapabilitiesRestrictedForCallerPermissions(
- nc, Process.myPid(), Process.myUid());
- assertTrue(result.getSubscriptionIds().isEmpty());
- }
-
- @Test
- public void testSubIdsExistWithNetworkFactoryPermission() throws Exception {
- mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED);
-
+ public void testSubIdsExist() throws Exception {
final Set<Integer> subIds = Collections.singleton(Process.myUid());
final NetworkCapabilities nc = new NetworkCapabilities();
nc.setSubscriptionIds(subIds);
@@ -17378,8 +17367,7 @@
}
@Test
- public void testNetworkRequestWithSubIdsWithNetworkFactoryPermission() throws Exception {
- mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED);
+ public void testNetworkRequestWithSubIds() throws Exception {
final PendingIntent pendingIntent = PendingIntent.getBroadcast(
mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE);
final NetworkCallback networkCallback1 = new NetworkCallback();
@@ -17395,21 +17383,6 @@
}
@Test
- public void testNetworkRequestWithSubIdsWithoutNetworkFactoryPermission() throws Exception {
- mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED);
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(
- mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE);
-
- final Class<SecurityException> expected = SecurityException.class;
- assertThrows(
- expected, () -> mCm.requestNetwork(getRequestWithSubIds(), new NetworkCallback()));
- assertThrows(expected, () -> mCm.requestNetwork(getRequestWithSubIds(), pendingIntent));
- assertThrows(
- expected,
- () -> mCm.registerNetworkCallback(getRequestWithSubIds(), new NetworkCallback()));
- }
-
- @Test
@IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
public void testCarrierConfigAppSendNetworkRequestForRestrictedWifi() throws Exception {
mServiceContext.setPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, PERMISSION_DENIED);
@@ -17544,6 +17517,47 @@
false /* expectUnavailable */,
true /* expectCapChanged */);
}
+
+ @Test
+ public void testAllowedUidsExistWithoutNetworkFactoryPermission() throws Exception {
+ // Make sure NETWORK_FACTORY permission is not granted.
+ mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED);
+ mServiceContext.setPermission(MANAGE_TEST_NETWORKS, PERMISSION_GRANTED);
+ final TestNetworkCallback cb = new TestNetworkCallback();
+ mCm.requestNetwork(new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(TRANSPORT_TEST)
+ .addTransportType(TRANSPORT_CELLULAR)
+ .build(),
+ cb);
+
+ final ArraySet<Integer> uids = new ArraySet<>();
+ uids.add(200);
+ final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_TEST)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .setAllowedUids(uids)
+ .setOwnerUid(Process.myUid())
+ .setAdministratorUids(new int[] {Process.myUid()})
+ .build();
+ final TestNetworkAgentWrapper agent = new TestNetworkAgentWrapper(TRANSPORT_TEST,
+ new LinkProperties(), nc);
+ agent.connect(true);
+ cb.expectAvailableThenValidatedCallbacks(agent);
+
+ uids.add(300);
+ uids.add(400);
+ nc.setAllowedUids(uids);
+ agent.setNetworkCapabilities(nc, true /* sendToConnectivityService */);
+ if (mDeps.isAtLeastT()) {
+ // AllowedUids is not cleared even without the NETWORK_FACTORY permission
+ // because the caller is the owner of the network.
+ cb.expectCaps(agent, c -> c.getAllowedUids().equals(uids));
+ } else {
+ cb.assertNoCallback();
+ }
+ }
+
@Test
public void testAllowedUids() throws Exception {
final int preferenceOrder =
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSCaptivePortalAppTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSCaptivePortalAppTest.kt
index be2b29c..0bad60d 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSCaptivePortalAppTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSCaptivePortalAppTest.kt
@@ -20,6 +20,7 @@
import android.content.Intent
import android.content.pm.PackageManager.PERMISSION_DENIED
import android.content.pm.PackageManager.PERMISSION_GRANTED
+import android.net.CaptivePortal
import android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN
import android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL
import android.net.IpPrefix
@@ -33,23 +34,23 @@
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED
import android.net.NetworkCapabilities.TRANSPORT_WIFI
-import android.net.NetworkStack
-import android.net.CaptivePortal
import android.net.NetworkRequest
import android.net.NetworkScore
import android.net.NetworkScore.KEEP_CONNECTED_FOR_TEST
+import android.net.NetworkStack
import android.net.RouteInfo
import android.os.Build
import android.os.Bundle
import androidx.test.filters.SmallTest
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
-import com.android.testutils.assertThrows
import com.android.testutils.TestableNetworkCallback
+import kotlin.test.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import kotlin.test.assertEquals
// This allows keeping all the networks connected without having to file individual requests
// for them.
@@ -95,16 +96,22 @@
captivePortalCallback.expectAvailableCallbacksUnvalidated(wifiAgent)
val signInIntent = startCaptivePortalApp(wifiAgent)
// Remove the granted permissions
- context.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
- PERMISSION_DENIED)
+ context.setPermission(
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ PERMISSION_DENIED
+ )
context.setPermission(NETWORK_STACK, PERMISSION_DENIED)
val captivePortal: CaptivePortal? = signInIntent.getParcelableExtra(EXTRA_CAPTIVE_PORTAL)
- assertThrows(SecurityException::class.java, { captivePortal?.reevaluateNetwork() })
+ captivePortal?.reevaluateNetwork()
+ verify(wifiAgent.networkMonitor, never()).forceReevaluation(anyInt())
}
private fun createWifiAgent(): CSAgentWrapper {
- return Agent(score = keepScore(), lp = lp(WIFI_IFACE),
- nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
+ return Agent(
+ score = keepScore(),
+ lp = lp(WIFI_IFACE),
+ nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)
+ )
}
private fun startCaptivePortalApp(networkAgent: CSAgentWrapper): Intent {
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSIngressDiscardRuleTests.kt b/tests/unit/java/com/android/server/connectivityservice/CSIngressDiscardRuleTests.kt
new file mode 100644
index 0000000..e8664c1
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivityservice/CSIngressDiscardRuleTests.kt
@@ -0,0 +1,289 @@
+/*
+ * 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.InetAddresses
+import android.net.LinkAddress
+import android.net.LinkProperties
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN
+import android.net.NetworkCapabilities.TRANSPORT_VPN
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.NetworkRequest
+import android.net.VpnManager.TYPE_VPN_SERVICE
+import android.net.VpnTransportInfo
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
+import com.android.testutils.TestableNetworkCallback
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.never
+import org.mockito.Mockito.timeout
+import org.mockito.Mockito.verify
+
+private const val VPN_IFNAME = "tun10041"
+private const val VPN_IFNAME2 = "tun10042"
+private const val WIFI_IFNAME = "wlan0"
+private const val TIMEOUT_MS = 1_000L
+private const val LONG_TIMEOUT_MS = 5_000
+
+private fun vpnNc() = NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_VPN)
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ .setTransportInfo(
+ VpnTransportInfo(
+ TYPE_VPN_SERVICE,
+ "MySession12345",
+ false /* bypassable */,
+ false /* longLivedTcpConnectionsExpensive */))
+ .build()
+
+private fun wifiNc() = NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ .build()
+
+private fun nr(transport: Int) = NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(transport).apply {
+ if (transport != TRANSPORT_VPN) {
+ addCapability(NET_CAPABILITY_NOT_VPN)
+ }
+ }.build()
+
+private fun lp(iface: String, vararg linkAddresses: LinkAddress) = LinkProperties().apply {
+ interfaceName = iface
+ for (linkAddress in linkAddresses) {
+ addLinkAddress(linkAddress)
+ }
+}
+
+@RunWith(DevSdkIgnoreRunner::class)
+@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
+class CSIngressDiscardRuleTests : CSTest() {
+ private val IPV6_ADDRESS = InetAddresses.parseNumericAddress("2001:db8:1::1")
+ private val IPV6_LINK_ADDRESS = LinkAddress(IPV6_ADDRESS, 64)
+ private val IPV6_ADDRESS2 = InetAddresses.parseNumericAddress("2001:db8:1::2")
+ private val IPV6_LINK_ADDRESS2 = LinkAddress(IPV6_ADDRESS2, 64)
+ private val IPV6_ADDRESS3 = InetAddresses.parseNumericAddress("2001:db8:1::3")
+ private val IPV6_LINK_ADDRESS3 = LinkAddress(IPV6_ADDRESS3, 64)
+ private val LOCAL_IPV6_ADDRRESS = InetAddresses.parseNumericAddress("fe80::1234")
+ private val LOCAL_IPV6_LINK_ADDRRESS = LinkAddress(LOCAL_IPV6_ADDRRESS, 64)
+
+ @Test
+ fun testVpnIngressDiscardRule_UpdateVpnAddress() {
+ // non-VPN network whose address will be not duplicated with VPN address
+ val wifiNc = wifiNc()
+ val wifiLp = lp(WIFI_IFNAME, IPV6_LINK_ADDRESS3)
+ val wifiAgent = Agent(nc = wifiNc, lp = wifiLp)
+ wifiAgent.connect()
+
+ val nr = nr(TRANSPORT_VPN)
+ val cb = TestableNetworkCallback()
+ cm.registerNetworkCallback(nr, cb)
+ val nc = vpnNc()
+ val lp = lp(VPN_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
+ val agent = Agent(nc = nc, lp = lp)
+ agent.connect()
+ cb.expectAvailableCallbacks(agent.network, validated = false)
+
+ // IngressDiscardRule is added to the VPN address
+ verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS, VPN_IFNAME)
+ verify(bpfNetMaps, never()).setIngressDiscardRule(LOCAL_IPV6_ADDRRESS, VPN_IFNAME)
+
+ // The VPN address is changed
+ val newLp = lp(VPN_IFNAME, IPV6_LINK_ADDRESS2, LOCAL_IPV6_LINK_ADDRRESS)
+ agent.sendLinkProperties(newLp)
+ cb.expect<LinkPropertiesChanged>(agent.network)
+
+ // IngressDiscardRule is removed from the old VPN address and added to the new VPN address
+ verify(bpfNetMaps).removeIngressDiscardRule(IPV6_ADDRESS)
+ verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS2, VPN_IFNAME)
+ verify(bpfNetMaps, never()).setIngressDiscardRule(LOCAL_IPV6_ADDRRESS, VPN_IFNAME)
+
+ agent.disconnect()
+ verify(bpfNetMaps, timeout(TIMEOUT_MS)).removeIngressDiscardRule(IPV6_ADDRESS2)
+
+ cm.unregisterNetworkCallback(cb)
+ }
+
+ @Test
+ fun testVpnIngressDiscardRule_UpdateInterfaceName() {
+ val inorder = inOrder(bpfNetMaps)
+
+ val nr = nr(TRANSPORT_VPN)
+ val cb = TestableNetworkCallback()
+ cm.registerNetworkCallback(nr, cb)
+ val nc = vpnNc()
+ val lp = lp(VPN_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
+ val agent = Agent(nc = nc, lp = lp)
+ agent.connect()
+ cb.expectAvailableCallbacks(agent.network, validated = false)
+
+ // IngressDiscardRule is added to the VPN address
+ inorder.verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS, VPN_IFNAME)
+ inorder.verifyNoMoreInteractions()
+
+ // The VPN interface name is changed
+ val newlp = lp(VPN_IFNAME2, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
+ agent.sendLinkProperties(newlp)
+ cb.expect<LinkPropertiesChanged>(agent.network)
+
+ // IngressDiscardRule is updated with the new interface name
+ inorder.verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS, VPN_IFNAME2)
+ inorder.verifyNoMoreInteractions()
+
+ agent.disconnect()
+ inorder.verify(bpfNetMaps, timeout(TIMEOUT_MS)).removeIngressDiscardRule(IPV6_ADDRESS)
+
+ cm.unregisterNetworkCallback(cb)
+ }
+
+ @Test
+ fun testVpnIngressDiscardRule_DuplicatedIpAddress_UpdateVpnAddress() {
+ val inorder = inOrder(bpfNetMaps)
+
+ val wifiNc = wifiNc()
+ val wifiLp = lp(WIFI_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
+ val wifiAgent = Agent(nc = wifiNc, lp = wifiLp)
+ wifiAgent.connect()
+
+ // IngressDiscardRule is not added to non-VPN interfaces
+ inorder.verify(bpfNetMaps, never()).setIngressDiscardRule(any(), any())
+
+ val nr = nr(TRANSPORT_VPN)
+ val cb = TestableNetworkCallback()
+ cm.requestNetwork(nr, cb)
+ val vpnNc = vpnNc()
+ val vpnLp = lp(VPN_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
+ val vpnAgent = Agent(nc = vpnNc, lp = vpnLp)
+ vpnAgent.connect()
+ cb.expectAvailableCallbacks(vpnAgent.network, validated = false)
+
+ // IngressDiscardRule is not added since the VPN address is duplicated with the Wi-Fi
+ // address
+ inorder.verify(bpfNetMaps, never()).setIngressDiscardRule(any(), any())
+
+ // The VPN address is changed to a different address from the Wi-Fi interface
+ val newVpnlp = lp(VPN_IFNAME, IPV6_LINK_ADDRESS2, LOCAL_IPV6_LINK_ADDRRESS)
+ vpnAgent.sendLinkProperties(newVpnlp)
+
+ // IngressDiscardRule is added to the VPN address since the VPN address is not duplicated
+ // with the Wi-Fi address
+ cb.expect<LinkPropertiesChanged>(vpnAgent.network)
+ inorder.verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS2, VPN_IFNAME)
+
+ // The VPN address is changed back to the same address as the Wi-Fi interface
+ vpnAgent.sendLinkProperties(vpnLp)
+ cb.expect<LinkPropertiesChanged>(vpnAgent.network)
+
+ // IngressDiscardRule for IPV6_ADDRESS2 is removed but IngressDiscardRule for
+ // IPV6_LINK_ADDRESS is not added since Wi-Fi also uses IPV6_LINK_ADDRESS
+ inorder.verify(bpfNetMaps).removeIngressDiscardRule(IPV6_ADDRESS2)
+ inorder.verifyNoMoreInteractions()
+
+ vpnAgent.disconnect()
+ inorder.verifyNoMoreInteractions()
+
+ cm.unregisterNetworkCallback(cb)
+ }
+
+ @Test
+ fun testVpnIngressDiscardRule_DuplicatedIpAddress_UpdateNonVpnAddress() {
+ val inorder = inOrder(bpfNetMaps)
+
+ val vpnNc = vpnNc()
+ val vpnLp = lp(VPN_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
+ val vpnAgent = Agent(nc = vpnNc, lp = vpnLp)
+ vpnAgent.connect()
+
+ // IngressDiscardRule is added to the VPN address
+ inorder.verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS, VPN_IFNAME)
+ inorder.verifyNoMoreInteractions()
+
+ val nr = nr(TRANSPORT_WIFI)
+ val cb = TestableNetworkCallback()
+ cm.requestNetwork(nr, cb)
+ val wifiNc = wifiNc()
+ val wifiLp = lp(WIFI_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
+ val wifiAgent = Agent(nc = wifiNc, lp = wifiLp)
+ wifiAgent.connect()
+ cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
+
+ // IngressDiscardRule is removed since the VPN address is duplicated with the Wi-Fi address
+ inorder.verify(bpfNetMaps).removeIngressDiscardRule(IPV6_ADDRESS)
+
+ // The Wi-Fi address is changed to a different address from the VPN interface
+ val newWifilp = lp(WIFI_IFNAME, IPV6_LINK_ADDRESS2, LOCAL_IPV6_LINK_ADDRRESS)
+ wifiAgent.sendLinkProperties(newWifilp)
+ cb.expect<LinkPropertiesChanged>(wifiAgent.network)
+
+ // IngressDiscardRule is added to the VPN address since the VPN address is not duplicated
+ // with the Wi-Fi address
+ inorder.verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS, VPN_IFNAME)
+ inorder.verifyNoMoreInteractions()
+
+ // The Wi-Fi address is changed back to the same address as the VPN interface
+ wifiAgent.sendLinkProperties(wifiLp)
+ cb.expect<LinkPropertiesChanged>(wifiAgent.network)
+
+ // IngressDiscardRule is removed since the VPN address is duplicated with the Wi-Fi address
+ inorder.verify(bpfNetMaps).removeIngressDiscardRule(IPV6_ADDRESS)
+
+ // IngressDiscardRule is added to the VPN address since Wi-Fi is disconnected
+ wifiAgent.disconnect()
+ inorder.verify(bpfNetMaps, timeout(TIMEOUT_MS))
+ .setIngressDiscardRule(IPV6_ADDRESS, VPN_IFNAME)
+
+ vpnAgent.disconnect()
+ inorder.verify(bpfNetMaps, timeout(TIMEOUT_MS)).removeIngressDiscardRule(IPV6_ADDRESS)
+
+ cm.unregisterNetworkCallback(cb)
+ }
+
+ @Test
+ fun testVpnIngressDiscardRule_UnregisterAfterReplacement() {
+ val wifiNc = wifiNc()
+ val wifiLp = lp(WIFI_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
+ val wifiAgent = Agent(nc = wifiNc, lp = wifiLp)
+ wifiAgent.connect()
+ wifiAgent.unregisterAfterReplacement(LONG_TIMEOUT_MS)
+ waitForIdle()
+
+ val vpnNc = vpnNc()
+ val vpnLp = lp(VPN_IFNAME, IPV6_LINK_ADDRESS, LOCAL_IPV6_LINK_ADDRRESS)
+ val vpnAgent = Agent(nc = vpnNc, lp = vpnLp)
+ vpnAgent.connect()
+
+ // IngressDiscardRule is added since the Wi-Fi network is destroyed
+ verify(bpfNetMaps).setIngressDiscardRule(IPV6_ADDRESS, VPN_IFNAME)
+
+ // IngressDiscardRule is removed since the VPN network is destroyed
+ vpnAgent.unregisterAfterReplacement(LONG_TIMEOUT_MS)
+ waitForIdle()
+ verify(bpfNetMaps).removeIngressDiscardRule(IPV6_ADDRESS)
+ }
+}
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 7007b16..13c5cbc 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt
@@ -185,6 +185,7 @@
fun sendLocalNetworkConfig(lnc: LocalNetworkConfig) = agent.sendLocalNetworkConfig(lnc)
fun sendNetworkCapabilities(nc: NetworkCapabilities) = agent.sendNetworkCapabilities(nc)
+ fun sendLinkProperties(lp: LinkProperties) = agent.sendLinkProperties(lp)
fun connectWithCaptivePortal(redirectUrl: String) {
setCaptivePortal(redirectUrl)
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
index 1966cb1..6c9871c 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
@@ -53,6 +53,7 @@
import android.os.UserHandle
import android.os.UserManager
import android.permission.PermissionManager.PermissionResult
+import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.testing.TestableContext
import androidx.test.platform.app.InstrumentationRegistry
@@ -131,8 +132,10 @@
init {
if (!SdkLevel.isAtLeastS()) {
- throw UnsupportedApiLevelException("CSTest subclasses must be annotated to only " +
- "run on S+, e.g. @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)")
+ throw UnsupportedApiLevelException(
+ "CSTest subclasses must be annotated to only " +
+ "run on S+, e.g. @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)"
+ )
}
}
@@ -150,6 +153,7 @@
it[ConnectivityService.DELAY_DESTROY_FROZEN_SOCKETS_VERSION] = true
it[ConnectivityService.ALLOW_SYSUI_CONNECTIVITY_REPORTS] = true
it[ConnectivityService.ALLOW_SATALLITE_NETWORK_FALLBACK] = true
+ it[ConnectivityFlags.INGRESS_TO_VPN_ADDRESS_FILTERING] = true
}
fun enableFeature(f: String) = enabledFeatures.set(f, true)
fun disableFeature(f: String) = enabledFeatures.set(f, false)
@@ -183,6 +187,7 @@
val telephonyManager = mock<TelephonyManager>().also {
doReturn(true).`when`(it).isDataCapable()
}
+ val subscriptionManager = mock<SubscriptionManager>()
val multicastRoutingCoordinatorService = mock<MulticastRoutingCoordinatorService>()
val satelliteAccessController = mock<SatelliteAccessController>()
@@ -252,8 +257,12 @@
AutomaticOnOffKeepaliveTracker(c, h, AOOKTDeps(c))
override fun makeMultinetworkPolicyTracker(c: Context, h: Handler, r: Runnable) =
- MultinetworkPolicyTracker(c, h, r,
- MultinetworkPolicyTrackerTestDependencies(connResources.get()))
+ MultinetworkPolicyTracker(
+ c,
+ h,
+ r,
+ MultinetworkPolicyTrackerTestDependencies(connResources.get())
+ )
override fun makeNetworkRequestStateStatsMetrics(c: Context) =
this@CSTest.networkRequestStateStatsMetrics
@@ -338,8 +347,12 @@
override fun enforceCallingOrSelfPermission(permission: String, message: String?) {
// If the permission result does not set in the mMockedPermissions, it will be
// considered as PERMISSION_GRANTED as existing design to prevent breaking other tests.
- val granted = checkMockedPermission(permission, Process.myPid(), Process.myUid(),
- PERMISSION_GRANTED)
+ val granted = checkMockedPermission(
+ permission,
+ Process.myPid(),
+ Process.myUid(),
+ PERMISSION_GRANTED
+ )
if (!granted.equals(PERMISSION_GRANTED)) {
throw SecurityException("[Test] permission denied: " + permission)
}
@@ -350,8 +363,12 @@
override fun checkCallingOrSelfPermission(permission: String) =
checkMockedPermission(permission, Process.myPid(), Process.myUid(), PERMISSION_GRANTED)
- private fun checkMockedPermission(permission: String, pid: Int, uid: Int, default: Int):
- Int {
+ private fun checkMockedPermission(
+ permission: String,
+ pid: Int,
+ uid: Int,
+ default: Int
+ ): Int {
val processSpecificKey = "$permission,$pid,$uid"
return mMockedPermissions[processSpecificKey]
?: mMockedPermissions[permission] ?: default
@@ -413,6 +430,7 @@
Context.ACTIVITY_SERVICE -> activityManager
Context.SYSTEM_CONFIG_SERVICE -> systemConfigManager
Context.TELEPHONY_SERVICE -> telephonyManager
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE -> subscriptionManager
Context.BATTERY_STATS_SERVICE -> batteryManager
Context.STATS_MANAGER -> null // Stats manager is final and can't be mocked
Context.APP_OPS_SERVICE -> appOpsManager
@@ -422,8 +440,7 @@
internal val orderedBroadcastAsUserHistory = ArrayTrackRecord<Intent>().newReadHead()
fun expectNoDataActivityBroadcast(timeoutMs: Int) {
- assertNull(orderedBroadcastAsUserHistory.poll(
- timeoutMs.toLong()) { intent -> true })
+ assertNull(orderedBroadcastAsUserHistory.poll(timeoutMs.toLong()))
}
override fun sendOrderedBroadcastAsUser(
diff --git a/thread/service/java/com/android/server/thread/NsdPublisher.java b/thread/service/java/com/android/server/thread/NsdPublisher.java
index 440c2c3..3c7a72b 100644
--- a/thread/service/java/com/android/server/thread/NsdPublisher.java
+++ b/thread/service/java/com/android/server/thread/NsdPublisher.java
@@ -204,8 +204,12 @@
}
}
- /** On ot-daemon died, unregister all registrations. */
- public void onOtDaemonDied() {
+ @Override
+ public void reset() {
+ mHandler.post(this::resetInternal);
+ }
+
+ private void resetInternal() {
checkOnHandlerThread();
for (int i = 0; i < mRegistrationListeners.size(); ++i) {
try {
@@ -222,6 +226,12 @@
}
}
mRegistrationListeners.clear();
+ mRegistrationJobs.clear();
+ }
+
+ /** On ot-daemon died, reset. */
+ public void onOtDaemonDied() {
+ reset();
}
// TODO: b/323300118 - Remove this mechanism when the race condition in NsdManager is fixed.
diff --git a/thread/tests/cts/Android.bp b/thread/tests/cts/Android.bp
index 5890d26..8cdf38d 100644
--- a/thread/tests/cts/Android.bp
+++ b/thread/tests/cts/Android.bp
@@ -19,7 +19,6 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-// TODO: add this test to the CTS test suite
android_test {
name: "CtsThreadNetworkTestCases",
min_sdk_version: "33",
@@ -30,6 +29,7 @@
"src/**/*.java",
],
test_suites: [
+ "cts",
"general-tests",
"mcts-tethering",
"mts-tethering",
diff --git a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
index 48e0c26..0591c87 100644
--- a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
@@ -104,6 +104,7 @@
private static final int CALLBACK_TIMEOUT_MILLIS = 1_000;
private static final int ENABLED_TIMEOUT_MILLIS = 2_000;
private static final int SERVICE_DISCOVERY_TIMEOUT_MILLIS = 30_000;
+ private static final int SERVICE_LOST_TIMEOUT_MILLIS = 20_000;
private static final String MESHCOP_SERVICE_TYPE = "_meshcop._udp";
private static final String THREAD_NETWORK_PRIVILEGED =
"android.permission.THREAD_NETWORK_PRIVILEGED";
@@ -869,7 +870,7 @@
discoverForServiceLost(MESHCOP_SERVICE_TYPE, serviceLostFuture);
setEnabledAndWait(mController, false);
try {
- serviceLostFuture.get(SERVICE_DISCOVERY_TIMEOUT_MILLIS, MILLISECONDS);
+ serviceLostFuture.get(SERVICE_LOST_TIMEOUT_MILLIS, MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException ignored) {
// It's fine if the service lost event didn't show up. The service may not ever be
// advertised.
@@ -877,7 +878,9 @@
mNsdManager.stopServiceDiscovery(listener);
}
- assertThrows(TimeoutException.class, () -> discoverService(MESHCOP_SERVICE_TYPE));
+ assertThrows(
+ TimeoutException.class,
+ () -> discoverService(MESHCOP_SERVICE_TYPE, SERVICE_LOST_TIMEOUT_MILLIS));
}
private static void dropAllPermissions() {
@@ -1095,6 +1098,12 @@
// Return the first discovered service instance.
private NsdServiceInfo discoverService(String serviceType) throws Exception {
+ return discoverService(serviceType, SERVICE_DISCOVERY_TIMEOUT_MILLIS);
+ }
+
+ // Return the first discovered service instance.
+ private NsdServiceInfo discoverService(String serviceType, int timeoutMilliseconds)
+ throws Exception {
CompletableFuture<NsdServiceInfo> serviceInfoFuture = new CompletableFuture<>();
NsdManager.DiscoveryListener listener =
new DefaultDiscoveryListener() {
@@ -1105,7 +1114,7 @@
};
mNsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, listener);
try {
- serviceInfoFuture.get(SERVICE_DISCOVERY_TIMEOUT_MILLIS, MILLISECONDS);
+ serviceInfoFuture.get(timeoutMilliseconds, MILLISECONDS);
} finally {
mNsdManager.stopServiceDiscovery(listener);
}
diff --git a/thread/tests/unit/src/com/android/server/thread/NsdPublisherTest.java b/thread/tests/unit/src/com/android/server/thread/NsdPublisherTest.java
index 54e89b1..d860166 100644
--- a/thread/tests/unit/src/com/android/server/thread/NsdPublisherTest.java
+++ b/thread/tests/unit/src/com/android/server/thread/NsdPublisherTest.java
@@ -25,6 +25,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -468,7 +469,7 @@
}
@Test
- public void onOtDaemonDied_unregisterAll() {
+ public void reset_unregisterAll() {
prepareTest();
DnsTxtAttribute txt1 = makeTxtAttribute("key1", List.of(0x01, 0x02));
@@ -540,7 +541,7 @@
actualRegistrationListenerCaptor.getAllValues().get(1);
actualListener3.onServiceRegistered(actualServiceInfoCaptor.getValue());
- mNsdPublisher.onOtDaemonDied();
+ mNsdPublisher.reset();
mTestLooper.dispatchAll();
verify(mMockNsdManager, times(1)).unregisterService(actualListener1);
@@ -548,6 +549,17 @@
verify(mMockNsdManager, times(1)).unregisterService(actualListener3);
}
+ @Test
+ public void onOtDaemonDied_resetIsCalled() {
+ prepareTest();
+ NsdPublisher spyNsdPublisher = spy(mNsdPublisher);
+
+ spyNsdPublisher.onOtDaemonDied();
+ mTestLooper.dispatchAll();
+
+ verify(spyNsdPublisher, times(1)).reset();
+ }
+
private static DnsTxtAttribute makeTxtAttribute(String name, List<Integer> value) {
DnsTxtAttribute txtAttribute = new DnsTxtAttribute();
diff --git a/tools/Android.bp b/tools/Android.bp
index 9216b5b..2c2ed14 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -83,6 +83,8 @@
],
data: [
"testdata/test-jarjar-excludes.txt",
+ // txt with Test classes to test they aren't included when added to jarjar excludes
+ "testdata/test-jarjar-excludes-testclass.txt",
// two unsupportedappusage lists with different classes to test using multiple lists
"testdata/test-unsupportedappusage.txt",
"testdata/test-other-unsupportedappusage.txt",
diff --git a/tools/gen_jarjar_test.py b/tools/gen_jarjar_test.py
index f5bf499..12038e9 100644
--- a/tools/gen_jarjar_test.py
+++ b/tools/gen_jarjar_test.py
@@ -84,6 +84,31 @@
'rule test.utils.TestUtilClass$TestInnerClassTest jarjar.prefix.@0\n',
'rule test.utils.TestUtilClass$TestInnerClassTest$* jarjar.prefix.@0\n'], lines)
+ def test_gen_rules_repeated_testclass_excluded(self):
+ args = gen_jarjar.parse_arguments([
+ "jarjar-rules-generator-testjavalib.jar",
+ "--prefix", "jarjar.prefix",
+ "--output", "test-output-rules.txt",
+ "--apistubs", "framework-connectivity.stubs.module_lib.jar",
+ "--unsupportedapi", ":testdata/test-unsupportedappusage.txt",
+ "--excludes", "testdata/test-jarjar-excludes-testclass.txt",
+ ])
+ gen_jarjar.make_jarjar_rules(args)
+
+ with open(args.output) as out:
+ lines = out.readlines()
+
+ self.maxDiff = None
+ self.assertListEqual([
+ 'rule android.net.IpSecTransform jarjar.prefix.@0\n',
+ 'rule test.unsupportedappusage.OtherUnsupportedUsageClass jarjar.prefix.@0\n',
+ 'rule test.unsupportedappusage.OtherUnsupportedUsageClassTest jarjar.prefix.@0\n',
+ 'rule test.unsupportedappusage.OtherUnsupportedUsageClassTest$* jarjar.prefix.@0\n',
+ 'rule test.utils.TestUtilClass jarjar.prefix.@0\n',
+ 'rule test.utils.TestUtilClass$TestInnerClass jarjar.prefix.@0\n',
+ 'rule test.utils.TestUtilClass$TestInnerClassTest jarjar.prefix.@0\n',
+ 'rule test.utils.TestUtilClass$TestInnerClassTest$* jarjar.prefix.@0\n'], lines)
+
if __name__ == '__main__':
# Need verbosity=2 for the test results parser to find results
diff --git a/tools/testdata/test-jarjar-excludes-testclass.txt b/tools/testdata/test-jarjar-excludes-testclass.txt
new file mode 100644
index 0000000..f7cc2cb
--- /dev/null
+++ b/tools/testdata/test-jarjar-excludes-testclass.txt
@@ -0,0 +1,7 @@
+# Test file for excluded classes
+test\.jarj.rexcluded\.JarjarExcludedCla.s
+test\.jarjarexcluded\.JarjarExcludedClass\$TestInnerCl.ss
+
+# Exclude actual test files
+test\.utils\.TestUtilClassTest
+android\.net\.IpSecTransformTest
\ No newline at end of file