Merge changes from topic "overlay-vendor-model-name" into main
* changes:
[Thread] support customize meshcop txt via resourceoverlay
[Thread] add resourceoverlay config for vendor and model name
diff --git a/Cronet/tests/common/Android.bp b/Cronet/tests/common/Android.bp
index edeb0b3..703f544 100644
--- a/Cronet/tests/common/Android.bp
+++ b/Cronet/tests/common/Android.bp
@@ -43,6 +43,7 @@
jni_libs: [
"cronet_aml_components_cronet_android_cronet_tests__testing",
"cronet_aml_third_party_netty_tcnative_netty_tcnative_so__testing",
+ "libnativecoverage",
],
data: [":cronet_javatests_resources"],
}
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 9e42d2b..b1e636d 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -6287,13 +6287,13 @@
throw new IllegalStateException(
"isUidNetworkingBlocked is not supported on pre-U devices");
}
- final BpfNetMapsReader reader = BpfNetMapsReader.getInstance();
+ final NetworkStackBpfNetMaps reader = NetworkStackBpfNetMaps.getInstance();
// Note that before V, the data saver status in bpf is written by ConnectivityService
// when receiving {@link #ACTION_RESTRICT_BACKGROUND_CHANGED}. Thus,
// the status is not synchronized.
// On V+, the data saver status is set by platform code when enabling/disabling
// data saver, which is synchronized.
- return reader.isUidNetworkingBlocked(uid, isNetworkMetered, reader.getDataSaverEnabled());
+ return reader.isUidNetworkingBlocked(uid, isNetworkMetered);
}
/** @hide */
diff --git a/framework/src/android/net/BpfNetMapsReader.java b/framework/src/android/net/NetworkStackBpfNetMaps.java
similarity index 90%
rename from framework/src/android/net/BpfNetMapsReader.java
rename to framework/src/android/net/NetworkStackBpfNetMaps.java
index ee422ab..346c997 100644
--- a/framework/src/android/net/BpfNetMapsReader.java
+++ b/framework/src/android/net/NetworkStackBpfNetMaps.java
@@ -46,12 +46,14 @@
import com.android.net.module.util.Struct.U8;
/**
- * A helper class to *read* java BpfMaps.
+ * A helper class to *read* java BpfMaps for network stack.
+ * BpfMap operations that are not used from network stack should be in
+ * {@link com.android.server.BpfNetMaps}
* @hide
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU) // BPF maps were only mainlined in T
-public class BpfNetMapsReader {
- private static final String TAG = BpfNetMapsReader.class.getSimpleName();
+public class NetworkStackBpfNetMaps {
+ private static final String TAG = NetworkStackBpfNetMaps.class.getSimpleName();
// Locally store the handle of bpf maps. The FileDescriptors are statically cached inside the
// BpfMap implementation.
@@ -86,15 +88,15 @@
}
private static class SingletonHolder {
- static final BpfNetMapsReader sInstance = new BpfNetMapsReader();
+ static final NetworkStackBpfNetMaps sInstance = new NetworkStackBpfNetMaps();
}
@NonNull
- public static BpfNetMapsReader getInstance() {
+ public static NetworkStackBpfNetMaps getInstance() {
return SingletonHolder.sInstance;
}
- private BpfNetMapsReader() {
+ private NetworkStackBpfNetMaps() {
this(new Dependencies());
}
@@ -102,10 +104,11 @@
// concurrent access, the test needs to use a non-static approach for dependency injection and
// mocking virtual bpf maps.
@VisibleForTesting
- public BpfNetMapsReader(@NonNull Dependencies deps) {
+ public NetworkStackBpfNetMaps(@NonNull Dependencies deps) {
if (!SdkLevel.isAtLeastT()) {
throw new UnsupportedOperationException(
- BpfNetMapsReader.class.getSimpleName() + " is not supported below Android T");
+ NetworkStackBpfNetMaps.class.getSimpleName()
+ + " is not supported below Android T");
}
mDeps = deps;
mConfigurationMap = mDeps.getConfigurationMap();
@@ -231,17 +234,17 @@
/**
* Return whether the network is blocked by firewall chains for the given uid.
*
+ * Note that {@link #getDataSaverEnabled()} has a latency before V.
+ *
* @param uid The target uid.
* @param isNetworkMetered Whether the target network is metered.
- * @param isDataSaverEnabled Whether the data saver is enabled.
*
* @return True if the network is blocked. Otherwise, false.
* @throws ServiceSpecificException if the read fails.
*
* @hide
*/
- public boolean isUidNetworkingBlocked(final int uid, boolean isNetworkMetered,
- boolean isDataSaverEnabled) {
+ public boolean isUidNetworkingBlocked(final int uid, boolean isNetworkMetered) {
throwIfPreT("isUidBlockedByFirewallChains is not available on pre-T devices");
final long uidRuleConfig;
@@ -264,12 +267,18 @@
if (!isNetworkMetered) return false;
if ((uidMatch & PENALTY_BOX_MATCH) != 0) return true;
if ((uidMatch & HAPPY_BOX_MATCH) != 0) return false;
- return isDataSaverEnabled;
+ return getDataSaverEnabled();
}
/**
* Get Data Saver enabled or disabled
*
+ * Note that before V, the data saver status in bpf is written by ConnectivityService
+ * when receiving {@link ConnectivityManager#ACTION_RESTRICT_BACKGROUND_CHANGED}. Thus,
+ * the status is not synchronized.
+ * On V+, the data saver status is set by platform code when enabling/disabling
+ * data saver, which is synchronized.
+ *
* @return whether Data Saver is enabled or disabled.
* @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure.
diff --git a/netbpfload/NetBpfLoad.cpp b/netbpfload/NetBpfLoad.cpp
index 0688e88..196b687 100644
--- a/netbpfload/NetBpfLoad.cpp
+++ b/netbpfload/NetBpfLoad.cpp
@@ -97,7 +97,7 @@
},
};
-int loadAllElfObjects(const android::bpf::Location& location) {
+int loadAllElfObjects(const unsigned int bpfloader_ver, const android::bpf::Location& location) {
int retVal = 0;
DIR* dir;
struct dirent* ent;
@@ -111,7 +111,7 @@
progPath += s;
bool critical;
- int ret = android::bpf::loadProg(progPath.c_str(), &critical, location);
+ int ret = android::bpf::loadProg(progPath.c_str(), &critical, bpfloader_ver, location);
if (ret) {
if (critical) retVal = ret;
ALOGE("Failed to load object: %s, ret: %s", progPath.c_str(), std::strerror(-ret));
@@ -355,9 +355,15 @@
// Thus we need to manually create the /sys/fs/bpf/loader subdirectory.
if (createSysFsBpfSubDir("loader")) return 1;
+ // Version of Network BpfLoader depends on the Android OS version
+ unsigned int bpfloader_ver = 42u; // [42] BPFLOADER_MAINLINE_VERSION
+ if (isAtLeastT) ++bpfloader_ver; // [43] BPFLOADER_MAINLINE_T_VERSION
+ if (isAtLeastU) ++bpfloader_ver; // [44] BPFLOADER_MAINLINE_U_VERSION
+ if (isAtLeastV) ++bpfloader_ver; // [45] BPFLOADER_MAINLINE_V_VERSION
+
// Load all ELF objects, create programs and maps, and pin them
for (const auto& location : locations) {
- if (loadAllElfObjects(location) != 0) {
+ if (loadAllElfObjects(bpfloader_ver, location) != 0) {
ALOGE("=== CRITICAL FAILURE LOADING BPF PROGRAMS FROM %s ===", location.dir);
ALOGE("If this triggers reliably, you're probably missing kernel options or patches.");
ALOGE("If this triggers randomly, you might be hitting some memory allocation "
diff --git a/netbpfload/loader.cpp b/netbpfload/loader.cpp
index 013309e..9dd0d2a 100644
--- a/netbpfload/loader.cpp
+++ b/netbpfload/loader.cpp
@@ -31,15 +31,6 @@
#include <sys/wait.h>
#include <unistd.h>
-// This is Network BpfLoader v0.42
-// WARNING: If you ever hit cherrypick conflicts here you're doing it wrong:
-// You are NOT allowed to cherrypick bpfloader related patches out of order.
-// (indeed: cherrypicking is probably a bad idea and you should merge instead)
-// Mainline supports ONLY the published versions of the bpfloader for each Android release.
-#define BPFLOADER_VERSION_MAJOR 0u
-#define BPFLOADER_VERSION_MINOR 42u
-#define BPFLOADER_VERSION ((BPFLOADER_VERSION_MAJOR << 16) | BPFLOADER_VERSION_MINOR)
-
#include "BpfSyscallWrappers.h"
#include "bpf/BpfUtils.h"
#include "bpf/bpf_map_def.h"
@@ -622,7 +613,8 @@
}
static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds,
- const char* prefix, const size_t sizeOfBpfMapDef) {
+ const char* prefix, const size_t sizeOfBpfMapDef,
+ const unsigned int bpfloader_ver) {
int ret;
vector<char> mdData;
vector<struct bpf_map_def> md;
@@ -664,14 +656,14 @@
for (int i = 0; i < (int)mapNames.size(); i++) {
if (md[i].zero != 0) abort();
- if (BPFLOADER_VERSION < md[i].bpfloader_min_ver) {
+ if (bpfloader_ver < md[i].bpfloader_min_ver) {
ALOGI("skipping map %s which requires bpfloader min ver 0x%05x", mapNames[i].c_str(),
md[i].bpfloader_min_ver);
mapFds.push_back(unique_fd());
continue;
}
- if (BPFLOADER_VERSION >= md[i].bpfloader_max_ver) {
+ if (bpfloader_ver >= md[i].bpfloader_max_ver) {
ALOGI("skipping map %s which requires bpfloader max ver 0x%05x", mapNames[i].c_str(),
md[i].bpfloader_max_ver);
mapFds.push_back(unique_fd());
@@ -922,7 +914,7 @@
}
static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const string& license,
- const char* prefix) {
+ const char* prefix, const unsigned int bpfloader_ver) {
unsigned kvers = kernelVersion();
if (!kvers) {
@@ -958,8 +950,8 @@
ALOGD("cs[%d].name:%s requires bpfloader version [0x%05x,0x%05x)", i, name.c_str(),
bpfMinVer, bpfMaxVer);
- if (BPFLOADER_VERSION < bpfMinVer) continue;
- if (BPFLOADER_VERSION >= bpfMaxVer) continue;
+ if (bpfloader_ver < bpfMinVer) continue;
+ if (bpfloader_ver >= bpfMaxVer) continue;
if ((cs[i].prog_def->ignore_on_eng && isEng()) ||
(cs[i].prog_def->ignore_on_user && isUser()) ||
@@ -1095,7 +1087,8 @@
return 0;
}
-int loadProg(const char* elfPath, bool* isCritical, const Location& location) {
+int loadProg(const char* const elfPath, bool* const isCritical, const unsigned int bpfloader_ver,
+ const Location& location) {
vector<char> license;
vector<char> critical;
vector<codeSection> cs;
@@ -1134,27 +1127,27 @@
readSectionUint("size_of_bpf_prog_def", elfFile, DEFAULT_SIZEOF_BPF_PROG_DEF);
// inclusive lower bound check
- if (BPFLOADER_VERSION < bpfLoaderMinVer) {
+ if (bpfloader_ver < bpfLoaderMinVer) {
ALOGI("BpfLoader version 0x%05x ignoring ELF object %s with min ver 0x%05x",
- BPFLOADER_VERSION, elfPath, bpfLoaderMinVer);
+ bpfloader_ver, elfPath, bpfLoaderMinVer);
return 0;
}
// exclusive upper bound check
- if (BPFLOADER_VERSION >= bpfLoaderMaxVer) {
+ if (bpfloader_ver >= bpfLoaderMaxVer) {
ALOGI("BpfLoader version 0x%05x ignoring ELF object %s with max ver 0x%05x",
- BPFLOADER_VERSION, elfPath, bpfLoaderMaxVer);
+ bpfloader_ver, elfPath, bpfLoaderMaxVer);
return 0;
}
- if (BPFLOADER_VERSION < bpfLoaderMinRequiredVer) {
+ if (bpfloader_ver < bpfLoaderMinRequiredVer) {
ALOGI("BpfLoader version 0x%05x failing due to ELF object %s with required min ver 0x%05x",
- BPFLOADER_VERSION, elfPath, bpfLoaderMinRequiredVer);
+ bpfloader_ver, elfPath, bpfLoaderMinRequiredVer);
return -1;
}
ALOGI("BpfLoader version 0x%05x processing ELF object %s with ver [0x%05x,0x%05x)",
- BPFLOADER_VERSION, elfPath, bpfLoaderMinVer, bpfLoaderMaxVer);
+ bpfloader_ver, elfPath, bpfLoaderMinVer, bpfLoaderMaxVer);
if (sizeOfBpfMapDef < DEFAULT_SIZEOF_BPF_MAP_DEF) {
ALOGE("sizeof(bpf_map_def) of %zu is too small (< %d)", sizeOfBpfMapDef,
@@ -1177,7 +1170,7 @@
/* Just for future debugging */
if (0) dumpAllCs(cs);
- ret = createMaps(elfPath, elfFile, mapFds, location.prefix, sizeOfBpfMapDef);
+ ret = createMaps(elfPath, elfFile, mapFds, location.prefix, sizeOfBpfMapDef, bpfloader_ver);
if (ret) {
ALOGE("Failed to create maps: (ret=%d) in %s", ret, elfPath);
return ret;
@@ -1188,7 +1181,7 @@
applyMapRelo(elfFile, mapFds, cs);
- ret = loadCodeSections(elfPath, cs, string(license.data()), location.prefix);
+ ret = loadCodeSections(elfPath, cs, string(license.data()), location.prefix, bpfloader_ver);
if (ret) ALOGE("Failed to load programs, loadCodeSections ret=%d", ret);
return ret;
diff --git a/netbpfload/loader.h b/netbpfload/loader.h
index b884637..4da6830 100644
--- a/netbpfload/loader.h
+++ b/netbpfload/loader.h
@@ -70,7 +70,8 @@
};
// BPF loader implementation. Loads an eBPF ELF object
-int loadProg(const char* elfPath, bool* isCritical, const Location &location = {});
+int loadProg(const char* elfPath, bool* isCritical, const unsigned int bpfloader_ver,
+ const Location &location = {});
// Exposed for testing
unsigned int readSectionUint(const char* name, std::ifstream& elfFile, unsigned int defVal);
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index a7fddd0..487f25c 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -49,8 +49,8 @@
import android.app.StatsManager;
import android.content.Context;
-import android.net.BpfNetMapsReader;
import android.net.INetd;
+import android.net.NetworkStackBpfNetMaps;
import android.net.UidOwnerValue;
import android.os.Build;
import android.os.RemoteException;
@@ -535,14 +535,11 @@
* @throws UnsupportedOperationException if called on pre-T devices.
* @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure.
- *
- * @deprecated Use {@link BpfNetMapsReader#isChainEnabled} instead.
*/
- // TODO: Migrate the callers to use {@link BpfNetMapsReader#isChainEnabled} instead.
@Deprecated
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public boolean isChainEnabled(final int childChain) {
- return BpfNetMapsReader.isChainEnabled(sConfigurationMap, childChain);
+ return NetworkStackBpfNetMaps.isChainEnabled(sConfigurationMap, childChain);
}
private Set<Integer> asSet(final int[] uids) {
@@ -635,12 +632,9 @@
* @throws UnsupportedOperationException if called on pre-T devices.
* @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure.
- *
- * @deprecated use {@link BpfNetMapsReader#getUidRule} instead.
*/
- // TODO: Migrate the callers to use {@link BpfNetMapsReader#getUidRule} instead.
public int getUidRule(final int childChain, final int uid) {
- return BpfNetMapsReader.getUidRule(sUidOwnerMap, childChain, uid);
+ return NetworkStackBpfNetMaps.getUidRule(sUidOwnerMap, childChain, uid);
}
private Set<Integer> getUidsMatchEnabled(final int childChain) throws ErrnoException {
diff --git a/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h b/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
index 92ddf44..53c67d5 100644
--- a/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
+++ b/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
@@ -45,9 +45,19 @@
// 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
+// Android Mainline - this bpfloader should eventually go back to T (or even S)
+// Note: this value (and the following +1u's) are hardcoded in NetBpfLoad.cpp
#define BPFLOADER_MAINLINE_VERSION 42u
+// Android Mainline BpfLoader when running on Android T
+#define BPFLOADER_MAINLINE_T_VERSION (BPFLOADER_MAINLINE_VERSION + 1u)
+
+// Android Mainline BpfLoader when running on Android U
+#define BPFLOADER_MAINLINE_U_VERSION (BPFLOADER_MAINLINE_T_VERSION + 1u)
+
+// Android Mainline BpfLoader when running on Android V
+#define BPFLOADER_MAINLINE_V_VERSION (BPFLOADER_MAINLINE_U_VERSION + 1u)
+
/* For mainline module use, you can #define BPFLOADER_{MIN/MAX}_VER
* before #include "bpf_helpers.h" to change which bpfloaders will
* process the resulting .o file.
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index a5d2f4a..2f88c41 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -72,17 +72,6 @@
],
}
-// Subset of services-core used to by ConnectivityService tests to test VPN realistically.
-// This is stripped by jarjar (see rules below) from other unrelated classes, so tests do not
-// include most classes from services-core, which are unrelated and cause wrong code coverage
-// calculations.
-java_library {
- name: "services.core-vpn",
- static_libs: ["services.core"],
- jarjar_rules: "vpn-jarjar-rules.txt",
- visibility: ["//visibility:private"],
-}
-
java_defaults {
name: "FrameworksNetTestsDefaults",
min_sdk_version: "30",
@@ -109,7 +98,6 @@
"platform-test-annotations",
"service-connectivity-pre-jarjar",
"service-connectivity-tiramisu-pre-jarjar",
- "services.core-vpn",
"testables",
"cts-net-utils",
],
diff --git a/tests/unit/java/android/net/BpfNetMapsReaderTest.kt b/tests/unit/java/android/net/NetworkStackBpfNetMapsTest.kt
similarity index 90%
rename from tests/unit/java/android/net/BpfNetMapsReaderTest.kt
rename to tests/unit/java/android/net/NetworkStackBpfNetMapsTest.kt
index 8919666..ca98269 100644
--- a/tests/unit/java/android/net/BpfNetMapsReaderTest.kt
+++ b/tests/unit/java/android/net/NetworkStackBpfNetMapsTest.kt
@@ -50,7 +50,7 @@
// pre-T devices does not support Bpf.
@RunWith(DevSdkIgnoreRunner::class)
@IgnoreUpTo(VERSION_CODES.S_V2)
-class BpfNetMapsReaderTest {
+class NetworkStackBpfNetMapsTest {
@Rule
@JvmField
val ignoreRule = DevSdkIgnoreRule()
@@ -58,14 +58,15 @@
private val testConfigurationMap: IBpfMap<S32, U32> = TestBpfMap()
private val testUidOwnerMap: IBpfMap<S32, UidOwnerValue> = TestBpfMap()
private val testDataSaverEnabledMap: IBpfMap<S32, U8> = TestBpfMap()
- private val bpfNetMapsReader = BpfNetMapsReader(
- TestDependencies(testConfigurationMap, testUidOwnerMap, testDataSaverEnabledMap))
+ private val bpfNetMapsReader = NetworkStackBpfNetMaps(
+ TestDependencies(testConfigurationMap, testUidOwnerMap, testDataSaverEnabledMap)
+ )
class TestDependencies(
private val configMap: IBpfMap<S32, U32>,
private val uidOwnerMap: IBpfMap<S32, UidOwnerValue>,
private val dataSaverEnabledMap: IBpfMap<S32, U8>
- ) : BpfNetMapsReader.Dependencies() {
+ ) : NetworkStackBpfNetMaps.Dependencies() {
override fun getConfigurationMap() = configMap
override fun getUidOwnerMap() = uidOwnerMap
override fun getDataSaverEnabledMap() = dataSaverEnabledMap
@@ -99,11 +100,16 @@
Modifier.isStatic(it.modifiers) && it.name.startsWith("FIREWALL_CHAIN_")
}
// Verify the size matches, this also verifies no common item in allow and deny chains.
- assertEquals(BpfNetMapsConstants.ALLOW_CHAINS.size +
- BpfNetMapsConstants.DENY_CHAINS.size, declaredChains.size)
+ assertEquals(
+ BpfNetMapsConstants.ALLOW_CHAINS.size +
+ BpfNetMapsConstants.DENY_CHAINS.size,
+ declaredChains.size
+ )
declaredChains.forEach {
- assertTrue(BpfNetMapsConstants.ALLOW_CHAINS.contains(it.get(null)) ||
- BpfNetMapsConstants.DENY_CHAINS.contains(it.get(null)))
+ assertTrue(
+ BpfNetMapsConstants.ALLOW_CHAINS.contains(it.get(null)) ||
+ BpfNetMapsConstants.DENY_CHAINS.contains(it.get(null))
+ )
}
}
@@ -117,11 +123,17 @@
testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(newConfig))
}
- fun isUidNetworkingBlocked(uid: Int, metered: Boolean = false, dataSaver: Boolean = false) =
- bpfNetMapsReader.isUidNetworkingBlocked(uid, metered, dataSaver)
+ private fun mockDataSaverEnabled(enabled: Boolean) {
+ val dataSaverValue = if (enabled) {DATA_SAVER_ENABLED} else {DATA_SAVER_DISABLED}
+ testDataSaverEnabledMap.updateEntry(DATA_SAVER_ENABLED_KEY, U8(dataSaverValue))
+ }
+
+ fun isUidNetworkingBlocked(uid: Int, metered: Boolean = false) =
+ bpfNetMapsReader.isUidNetworkingBlocked(uid, metered)
@Test
fun testIsUidNetworkingBlockedByFirewallChains_allowChain() {
+ mockDataSaverEnabled(enabled = false)
// With everything disabled by default, verify the return value is false.
testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
assertFalse(isUidNetworkingBlocked(TEST_UID1))
@@ -141,6 +153,7 @@
@Test
fun testIsUidNetworkingBlockedByFirewallChains_denyChain() {
+ mockDataSaverEnabled(enabled = false)
// Enable standby chain but does not provide denied list. Verify the network is allowed
// for all uids.
testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
@@ -162,12 +175,14 @@
testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_POWERSAVE, true)
mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_STANDBY, true)
+ mockDataSaverEnabled(enabled = false)
assertTrue(isUidNetworkingBlocked(TEST_UID1))
}
@IgnoreUpTo(VERSION_CODES.S_V2)
@Test
fun testIsUidNetworkingBlockedByDataSaver() {
+ mockDataSaverEnabled(enabled = false)
// With everything disabled by default, verify the return value is false.
testConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, U32(0))
assertFalse(isUidNetworkingBlocked(TEST_UID1, metered = true))
@@ -180,10 +195,11 @@
// Enable data saver, verify the network is blocked for uid1, uid2, but uid3 in happy box
// is not affected.
+ mockDataSaverEnabled(enabled = true)
testUidOwnerMap.updateEntry(S32(TEST_UID3), UidOwnerValue(NO_IIF, HAPPY_BOX_MATCH))
- assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true, dataSaver = true))
- assertTrue(isUidNetworkingBlocked(TEST_UID2, metered = true, dataSaver = true))
- assertFalse(isUidNetworkingBlocked(TEST_UID3, metered = true, dataSaver = true))
+ assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true))
+ assertTrue(isUidNetworkingBlocked(TEST_UID2, metered = true))
+ assertFalse(isUidNetworkingBlocked(TEST_UID3, metered = true))
// Add uid1 to happy box as well, verify nothing is changed because penalty box has higher
// priority.
@@ -191,18 +207,19 @@
S32(TEST_UID1),
UidOwnerValue(NO_IIF, PENALTY_BOX_MATCH or HAPPY_BOX_MATCH)
)
- assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true, dataSaver = true))
- assertTrue(isUidNetworkingBlocked(TEST_UID2, metered = true, dataSaver = true))
- assertFalse(isUidNetworkingBlocked(TEST_UID3, metered = true, dataSaver = true))
+ assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true))
+ assertTrue(isUidNetworkingBlocked(TEST_UID2, metered = true))
+ assertFalse(isUidNetworkingBlocked(TEST_UID3, metered = true))
// Enable doze mode, verify uid3 is blocked even if it is in happy box.
mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_DOZABLE, true)
- assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true, dataSaver = true))
- assertTrue(isUidNetworkingBlocked(TEST_UID2, metered = true, dataSaver = true))
- assertTrue(isUidNetworkingBlocked(TEST_UID3, metered = true, dataSaver = true))
+ assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true))
+ assertTrue(isUidNetworkingBlocked(TEST_UID2, metered = true))
+ assertTrue(isUidNetworkingBlocked(TEST_UID3, metered = true))
// Disable doze mode and data saver, only uid1 which is in penalty box is blocked.
mockChainEnabled(ConnectivityManager.FIREWALL_CHAIN_DOZABLE, false)
+ mockDataSaverEnabled(enabled = false)
assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true))
assertFalse(isUidNetworkingBlocked(TEST_UID2, metered = true))
assertFalse(isUidNetworkingBlocked(TEST_UID3, metered = true))
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index f5eee42..8c30776 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -368,7 +368,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.security.Credentials;
import android.system.Os;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -389,7 +388,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnProfile;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
@@ -424,7 +422,6 @@
import com.android.server.connectivity.SatelliteAccessController;
import com.android.server.connectivity.TcpKeepaliveController;
import com.android.server.connectivity.UidRangeUtils;
-import com.android.server.connectivity.VpnProfileStore;
import com.android.server.net.NetworkPinner;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -464,7 +461,6 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -631,7 +627,6 @@
@Mock TelephonyManager mTelephonyManager;
@Mock EthernetManager mEthernetManager;
@Mock NetworkPolicyManager mNetworkPolicyManager;
- @Mock VpnProfileStore mVpnProfileStore;
@Mock SystemConfigManager mSystemConfigManager;
@Mock DevicePolicyManager mDevicePolicyManager;
@Mock Resources mResources;
@@ -1667,23 +1662,11 @@
waitForIdle();
}
- public void startLegacyVpnPrivileged(VpnProfile profile) {
- switch (profile.type) {
- case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
- case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
- case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
- case VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS:
- startPlatformVpn();
- break;
- case VpnProfile.TYPE_L2TP_IPSEC_PSK:
- case VpnProfile.TYPE_L2TP_IPSEC_RSA:
- case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
- case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
- case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
- startLegacyVpn();
- break;
- default:
- fail("Unknown VPN profile type");
+ public void startLegacyVpnPrivileged(boolean isIkev2Vpn) {
+ if (isIkev2Vpn) {
+ startPlatformVpn();
+ } else {
+ startLegacyVpn();
}
}
@@ -10213,24 +10196,6 @@
doAsUid(Process.SYSTEM_UID, () -> mCm.unregisterNetworkCallback(perUidCb));
}
- private VpnProfile setupLockdownVpn(int profileType) {
- final String profileName = "testVpnProfile";
- final byte[] profileTag = profileName.getBytes(StandardCharsets.UTF_8);
- doReturn(profileTag).when(mVpnProfileStore).get(Credentials.LOCKDOWN_VPN);
-
- final VpnProfile profile = new VpnProfile(profileName);
- profile.name = "My VPN";
- profile.server = "192.0.2.1";
- profile.dnsServers = "8.8.8.8";
- profile.ipsecIdentifier = "My ipsecIdentifier";
- profile.ipsecSecret = "My PSK";
- profile.type = profileType;
- final byte[] encodedProfile = profile.encode();
- doReturn(encodedProfile).when(mVpnProfileStore).get(Credentials.VPN + profileName);
-
- return profile;
- }
-
private void establishLegacyLockdownVpn(Network underlying) throws Exception {
// The legacy lockdown VPN only supports userId 0, and must have an underlying network.
assertNotNull(underlying);
@@ -10242,7 +10207,7 @@
mMockVpn.connect(true);
}
- private void doTestLockdownVpn(VpnProfile profile, boolean expectSetVpnDefaultForUids)
+ private void doTestLockdownVpn(boolean isIkev2Vpn)
throws Exception {
mServiceContext.setPermission(
Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
@@ -10280,8 +10245,8 @@
b.expectBroadcast();
// Simulate LockdownVpnTracker attempting to start the VPN since it received the
// systemDefault callback.
- mMockVpn.startLegacyVpnPrivileged(profile);
- if (expectSetVpnDefaultForUids) {
+ mMockVpn.startLegacyVpnPrivileged(isIkev2Vpn);
+ if (isIkev2Vpn) {
// setVpnDefaultForUids() releases the original network request and creates a VPN
// request so LOST callback is received.
defaultCallback.expect(LOST, mCellAgent);
@@ -10305,7 +10270,7 @@
final NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
b2.expectBroadcast();
b3.expectBroadcast();
- if (expectSetVpnDefaultForUids) {
+ if (isIkev2Vpn) {
// Due to the VPN default request, getActiveNetworkInfo() gets the VPN network as the
// network satisfier which has TYPE_VPN.
assertActiveNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
@@ -10351,14 +10316,15 @@
// callback with different network.
final ExpectedBroadcast b6 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
mMockVpn.stopVpnRunnerPrivileged();
- mMockVpn.startLegacyVpnPrivileged(profile);
+
+ mMockVpn.startLegacyVpnPrivileged(isIkev2Vpn);
// VPN network is disconnected (to restart)
callback.expect(LOST, mMockVpn);
defaultCallback.expect(LOST, mMockVpn);
// The network preference is cleared when VPN is disconnected so it receives callbacks for
// the system-wide default.
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiAgent);
- if (expectSetVpnDefaultForUids) {
+ if (isIkev2Vpn) {
// setVpnDefaultForUids() releases the original network request and creates a VPN
// request so LOST callback is received.
defaultCallback.expect(LOST, mWiFiAgent);
@@ -10367,7 +10333,7 @@
b6.expectBroadcast();
// While the VPN is reconnecting on the new network, everything is blocked.
- if (expectSetVpnDefaultForUids) {
+ if (isIkev2Vpn) {
// Due to the VPN default request, getActiveNetworkInfo() gets the mNoServiceNetwork
// as the network satisfier.
assertNull(mCm.getActiveNetworkInfo());
@@ -10388,7 +10354,7 @@
systemDefaultCallback.assertNoCallback();
b7.expectBroadcast();
b8.expectBroadcast();
- if (expectSetVpnDefaultForUids) {
+ if (isIkev2Vpn) {
// Due to the VPN default request, getActiveNetworkInfo() gets the VPN network as the
// network satisfier which has TYPE_VPN.
assertActiveNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
@@ -10414,7 +10380,7 @@
defaultCallback.assertNoCallback();
systemDefaultCallback.assertNoCallback();
- if (expectSetVpnDefaultForUids) {
+ if (isIkev2Vpn) {
// Due to the VPN default request, getActiveNetworkInfo() gets the VPN network as the
// network satisfier which has TYPE_VPN.
assertActiveNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
@@ -10455,14 +10421,12 @@
@Test
public void testLockdownVpn_LegacyVpnRunner() throws Exception {
- final VpnProfile profile = setupLockdownVpn(VpnProfile.TYPE_IPSEC_XAUTH_PSK);
- doTestLockdownVpn(profile, false /* expectSetVpnDefaultForUids */);
+ doTestLockdownVpn(false /* isIkev2Vpn */);
}
@Test
public void testLockdownVpn_Ikev2VpnRunner() throws Exception {
- final VpnProfile profile = setupLockdownVpn(VpnProfile.TYPE_IKEV2_IPSEC_PSK);
- doTestLockdownVpn(profile, true /* expectSetVpnDefaultForUids */);
+ doTestLockdownVpn(true /* isIkev2Vpn */);
}
@Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
diff --git a/tests/unit/vpn-jarjar-rules.txt b/tests/unit/vpn-jarjar-rules.txt
deleted file mode 100644
index f74eab8..0000000
--- a/tests/unit/vpn-jarjar-rules.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# Only keep classes imported by ConnectivityServiceTest
-keep com.android.server.connectivity.VpnProfileStore
diff --git a/thread/demoapp/Android.bp b/thread/demoapp/Android.bp
index fcfd469..00f8090 100644
--- a/thread/demoapp/Android.bp
+++ b/thread/demoapp/Android.bp
@@ -34,7 +34,17 @@
libs: [
"framework-connectivity-t",
],
+ required: [
+ "privapp-permissions-com.android.threadnetwork.demoapp",
+ ],
certificate: "platform",
privileged: true,
platform_apis: true,
}
+
+prebuilt_etc {
+ name: "privapp-permissions-com.android.threadnetwork.demoapp",
+ src: "privapp-permissions-com.android.threadnetwork.demoapp.xml",
+ sub_dir: "permissions",
+ filename_from_src: true,
+}
diff --git a/thread/demoapp/privapp-permissions-com.android.threadnetwork.demoapp.xml b/thread/demoapp/privapp-permissions-com.android.threadnetwork.demoapp.xml
new file mode 100644
index 0000000..1995e60
--- /dev/null
+++ b/thread/demoapp/privapp-permissions-com.android.threadnetwork.demoapp.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 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.
+-->
+
+<!-- The privileged permissions needed by the com.android.threadnetwork.demoapp app. -->
+<permissions>
+ <privapp-permissions package="com.android.threadnetwork.demoapp">
+ <permission name="android.permission.THREAD_NETWORK_PRIVILEGED" />
+ </privapp-permissions>
+</permissions>
diff --git a/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java b/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
index baf716f..353db10 100644
--- a/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
+++ b/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
@@ -17,10 +17,7 @@
package android.net.thread;
import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
-import static android.Manifest.permission.NETWORK_SETTINGS;
-import static android.net.thread.ThreadNetworkManager.PERMISSION_THREAD_NETWORK_PRIVILEGED;
import static android.net.thread.utils.IntegrationTestUtils.JOIN_TIMEOUT;
-import static android.net.thread.utils.IntegrationTestUtils.RESTART_JOIN_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.isExpectedIcmpv6Packet;
import static android.net.thread.utils.IntegrationTestUtils.isFromIpv6Source;
import static android.net.thread.utils.IntegrationTestUtils.isInMulticastGroup;
@@ -36,13 +33,14 @@
import static com.android.testutils.TestPermissionUtil.runAsShell;
import static com.google.common.io.BaseEncoding.base16;
-import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static java.util.Objects.requireNonNull;
+
import android.content.Context;
import android.net.InetAddresses;
import android.net.LinkProperties;
@@ -54,6 +52,7 @@
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresIpv6MulticastRouting;
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresSimulationThreadDevice;
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
+import android.net.thread.utils.ThreadNetworkControllerWrapper;
import android.os.Handler;
import android.os.HandlerThread;
@@ -74,9 +73,6 @@
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
/** Integration test cases for Thread Border Routing feature. */
@@ -108,7 +104,8 @@
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
private final Context mContext = ApplicationProvider.getApplicationContext();
- private ThreadNetworkController mController;
+ private final ThreadNetworkControllerWrapper mController =
+ ThreadNetworkControllerWrapper.newInstance(mContext);
private OtDaemonController mOtCtl;
private HandlerThread mHandlerThread;
private Handler mHandler;
@@ -119,11 +116,6 @@
@Before
public void setUp() throws Exception {
- final ThreadNetworkManager manager = mContext.getSystemService(ThreadNetworkManager.class);
- if (manager != null) {
- mController = manager.getAllThreadNetworkControllers().get(0);
- }
-
// TODO: b/323301831 - This is a workaround to avoid unnecessary delay to re-form a network
mOtCtl = new OtDaemonController();
mOtCtl.factoryReset();
@@ -134,9 +126,7 @@
mFtds = new ArrayList<>();
setUpInfraNetwork();
-
- // BR forms a network.
- startBrLeader();
+ mController.joinAndWait(DEFAULT_DATASET);
// Creates a infra network device.
mInfraNetworkReader = newPacketReader(mInfraNetworkTracker.getTestIface(), mHandler);
@@ -150,16 +140,8 @@
@After
public void tearDown() throws Exception {
- runAsShell(
- PERMISSION_THREAD_NETWORK_PRIVILEGED,
- NETWORK_SETTINGS,
- () -> {
- CountDownLatch latch = new CountDownLatch(2);
- mController.setTestNetworkAsUpstream(
- null, directExecutor(), v -> latch.countDown());
- mController.leave(directExecutor(), v -> latch.countDown());
- latch.await(10, TimeUnit.SECONDS);
- });
+ mController.setTestNetworkAsUpstreamAndWait(null);
+ mController.leaveAndWait();
tearDownInfraNetwork();
mHandlerThread.quitSafely();
@@ -205,9 +187,6 @@
* </pre>
*/
- // Form the network.
- mOtCtl.factoryReset();
- startBrLeader();
startInfraDevice();
FullThreadDevice ftd = mFtds.get(0);
startFtdChild(ftd);
@@ -229,12 +208,10 @@
* </pre>
*/
- // Creates a Full Thread Device (FTD) and lets it join the network.
FullThreadDevice ftd = mFtds.get(0);
startFtdChild(ftd);
- Inet6Address ftdOmr = ftd.getOmrAddress();
- Inet6Address ftdMlEid = ftd.getMlEid();
- assertNotNull(ftdMlEid);
+ Inet6Address ftdOmr = requireNonNull(ftd.getOmrAddress());
+ Inet6Address ftdMlEid = requireNonNull(ftd.getMlEid());
ftd.udpBind(ftdOmr, 12345);
sendUdpMessage(ftdOmr, 12345, "aaaaaaaa");
@@ -588,38 +565,21 @@
pollForPacketOnInfraNetwork(ICMPV6_ECHO_REQUEST_TYPE, ftdOmr, GROUP_ADDR_SCOPE_4));
}
- private void setUpInfraNetwork() {
+ private void setUpInfraNetwork() throws Exception {
mInfraNetworkTracker =
runAsShell(
MANAGE_TEST_NETWORKS,
() ->
initTestNetwork(
mContext, new LinkProperties(), 5000 /* timeoutMs */));
- runAsShell(
- PERMISSION_THREAD_NETWORK_PRIVILEGED,
- NETWORK_SETTINGS,
- () -> {
- CompletableFuture<Void> future = new CompletableFuture<>();
- mController.setTestNetworkAsUpstream(
- mInfraNetworkTracker.getTestIface().getInterfaceName(),
- directExecutor(),
- future::complete);
- future.get(5, TimeUnit.SECONDS);
- });
+ mController.setTestNetworkAsUpstreamAndWait(
+ mInfraNetworkTracker.getTestIface().getInterfaceName());
}
private void tearDownInfraNetwork() {
runAsShell(MANAGE_TEST_NETWORKS, () -> mInfraNetworkTracker.teardown());
}
- private void startBrLeader() throws Exception {
- CompletableFuture<Void> joinFuture = new CompletableFuture<>();
- runAsShell(
- PERMISSION_THREAD_NETWORK_PRIVILEGED,
- () -> mController.join(DEFAULT_DATASET, directExecutor(), joinFuture::complete));
- joinFuture.get(RESTART_JOIN_TIMEOUT.toSeconds(), TimeUnit.SECONDS);
- }
-
private void startFtdChild(FullThreadDevice ftd) throws Exception {
ftd.factoryReset();
ftd.joinNetwork(DEFAULT_DATASET);
diff --git a/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java b/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
index 3604714..39a1671 100644
--- a/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
+++ b/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
@@ -16,11 +16,8 @@
package android.net.thread;
-import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.net.InetAddresses.parseNumericAddress;
-import static android.net.thread.ThreadNetworkManager.PERMISSION_THREAD_NETWORK_PRIVILEGED;
import static android.net.thread.utils.IntegrationTestUtils.JOIN_TIMEOUT;
-import static android.net.thread.utils.IntegrationTestUtils.RESTART_JOIN_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.SERVICE_DISCOVERY_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.discoverForServiceLost;
import static android.net.thread.utils.IntegrationTestUtils.discoverService;
@@ -28,17 +25,13 @@
import static android.net.thread.utils.IntegrationTestUtils.resolveServiceUntil;
import static android.net.thread.utils.IntegrationTestUtils.waitFor;
-import static com.android.testutils.TestPermissionUtil.runAsShell;
-
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static org.junit.Assert.assertThrows;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
import android.content.Context;
import android.net.nsd.NsdManager;
@@ -48,6 +41,7 @@
import android.net.thread.utils.ThreadFeatureCheckerRule;
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresSimulationThreadDevice;
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
+import android.net.thread.utils.ThreadNetworkControllerWrapper;
import android.os.HandlerThread;
import androidx.test.core.app.ApplicationProvider;
@@ -100,27 +94,18 @@
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
private final Context mContext = ApplicationProvider.getApplicationContext();
+ private final ThreadNetworkControllerWrapper mController =
+ ThreadNetworkControllerWrapper.newInstance(mContext);
private HandlerThread mHandlerThread;
- private ThreadNetworkController mController;
private NsdManager mNsdManager;
private TapTestNetworkTracker mTestNetworkTracker;
private List<FullThreadDevice> mFtds;
@Before
public void setUp() throws Exception {
- final ThreadNetworkManager manager = mContext.getSystemService(ThreadNetworkManager.class);
- if (manager != null) {
- mController = manager.getAllThreadNetworkControllers().get(0);
- }
- // BR forms a network.
- CompletableFuture<Void> joinFuture = new CompletableFuture<>();
- runAsShell(
- PERMISSION_THREAD_NETWORK_PRIVILEGED,
- () -> mController.join(DEFAULT_DATASET, directExecutor(), joinFuture::complete));
- joinFuture.get(RESTART_JOIN_TIMEOUT.toMillis(), MILLISECONDS);
-
+ mController.joinAndWait(DEFAULT_DATASET);
mNsdManager = mContext.getSystemService(NsdManager.class);
mHandlerThread = new HandlerThread(TAG);
@@ -128,17 +113,8 @@
mTestNetworkTracker = new TapTestNetworkTracker(mContext, mHandlerThread.getLooper());
assertThat(mTestNetworkTracker).isNotNull();
- runAsShell(
- PERMISSION_THREAD_NETWORK_PRIVILEGED,
- NETWORK_SETTINGS,
- () -> {
- CompletableFuture<Void> future = new CompletableFuture<>();
- mController.setTestNetworkAsUpstream(
- mTestNetworkTracker.getInterfaceName(),
- directExecutor(),
- v -> future.complete(null));
- future.get(5, SECONDS);
- });
+ mController.setTestNetworkAsUpstreamAndWait(mTestNetworkTracker.getInterfaceName());
+
// Create the FTDs in setUp() so that the FTDs can be safely released in tearDown().
// Don't create new FTDs in test cases.
mFtds = new ArrayList<>();
@@ -165,18 +141,8 @@
mHandlerThread.quitSafely();
mHandlerThread.join();
}
- runAsShell(
- PERMISSION_THREAD_NETWORK_PRIVILEGED,
- NETWORK_SETTINGS,
- () -> {
- CompletableFuture<Void> setUpstreamFuture = new CompletableFuture<>();
- CompletableFuture<Void> leaveFuture = new CompletableFuture<>();
- mController.setTestNetworkAsUpstream(
- null, directExecutor(), v -> setUpstreamFuture.complete(null));
- mController.leave(directExecutor(), v -> leaveFuture.complete(null));
- setUpstreamFuture.get(5, SECONDS);
- leaveFuture.get(5, SECONDS);
- });
+ mController.setTestNetworkAsUpstreamAndWait(null);
+ mController.leaveAndWait();
}
@Test
diff --git a/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java b/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
index 9585d7d..4a006cf 100644
--- a/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
+++ b/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
@@ -16,31 +16,22 @@
package android.net.thread;
-import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_DETACHED;
import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_LEADER;
import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_STOPPED;
-import static android.net.thread.ThreadNetworkManager.PERMISSION_THREAD_NETWORK_PRIVILEGED;
import static android.net.thread.utils.IntegrationTestUtils.CALLBACK_TIMEOUT;
-import static android.net.thread.utils.IntegrationTestUtils.LEAVE_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.RESTART_JOIN_TIMEOUT;
-import static android.net.thread.utils.IntegrationTestUtils.waitForStateAnyOf;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-import static com.android.testutils.TestPermissionUtil.runAsShell;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-
-import android.annotation.Nullable;
import android.content.Context;
-import android.net.thread.ThreadNetworkController.StateCallback;
import android.net.thread.utils.OtDaemonController;
import android.net.thread.utils.ThreadFeatureCheckerRule;
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
+import android.net.thread.utils.ThreadNetworkControllerWrapper;
import android.os.SystemClock;
import androidx.test.core.app.ApplicationProvider;
@@ -55,7 +46,6 @@
import java.net.Inet6Address;
import java.util.List;
-import java.util.concurrent.CompletableFuture;
/** Tests for E2E Android Thread integration with ot-daemon, ConnectivityService, etc.. */
@LargeTest
@@ -76,17 +66,14 @@
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
private final Context mContext = ApplicationProvider.getApplicationContext();
- private ThreadNetworkController mController;
+ private final ThreadNetworkControllerWrapper mController =
+ ThreadNetworkControllerWrapper.newInstance(mContext);
private OtDaemonController mOtCtl;
@Before
public void setUp() throws Exception {
- mController =
- mContext.getSystemService(ThreadNetworkManager.class)
- .getAllThreadNetworkControllers()
- .get(0);
mOtCtl = new OtDaemonController();
- leaveAndWait(mController);
+ mController.leaveAndWait();
// TODO: b/323301831 - This is a workaround to avoid unnecessary delay to re-form a network
mOtCtl.factoryReset();
@@ -94,43 +81,43 @@
@After
public void tearDown() throws Exception {
- setTestUpStreamNetworkAndWait(mController, null);
- leaveAndWait(mController);
+ mController.setTestNetworkAsUpstreamAndWait(null);
+ mController.leaveAndWait();
}
@Test
public void otDaemonRestart_notJoinedAndStopped_deviceRoleIsStopped() throws Exception {
- leaveAndWait(mController);
+ mController.leaveAndWait();
runShellCommand("stop ot-daemon");
// TODO(b/323331973): the sleep is needed to workaround the race conditions
SystemClock.sleep(200);
- waitForStateAnyOf(mController, List.of(DEVICE_ROLE_STOPPED), CALLBACK_TIMEOUT);
+ mController.waitForRole(DEVICE_ROLE_STOPPED, CALLBACK_TIMEOUT);
}
@Test
public void otDaemonRestart_JoinedNetworkAndStopped_autoRejoined() throws Exception {
- joinAndWait(mController, DEFAULT_DATASET);
+ mController.joinAndWait(DEFAULT_DATASET);
runShellCommand("stop ot-daemon");
- waitForStateAnyOf(mController, List.of(DEVICE_ROLE_DETACHED), CALLBACK_TIMEOUT);
- waitForStateAnyOf(mController, List.of(DEVICE_ROLE_LEADER), RESTART_JOIN_TIMEOUT);
+ mController.waitForRole(DEVICE_ROLE_DETACHED, CALLBACK_TIMEOUT);
+ mController.waitForRole(DEVICE_ROLE_LEADER, RESTART_JOIN_TIMEOUT);
}
@Test
public void otDaemonFactoryReset_deviceRoleIsStopped() throws Exception {
- joinAndWait(mController, DEFAULT_DATASET);
+ mController.joinAndWait(DEFAULT_DATASET);
mOtCtl.factoryReset();
- assertThat(getDeviceRole(mController)).isEqualTo(DEVICE_ROLE_STOPPED);
+ assertThat(mController.getDeviceRole()).isEqualTo(DEVICE_ROLE_STOPPED);
}
@Test
public void otDaemonFactoryReset_addressesRemoved() throws Exception {
- joinAndWait(mController, DEFAULT_DATASET);
+ mController.joinAndWait(DEFAULT_DATASET);
mOtCtl.factoryReset();
String ifconfig = runShellCommand("ifconfig thread-wpan");
@@ -140,7 +127,7 @@
@Test
public void tunInterface_joinedNetwork_otAddressesAddedToTunInterface() throws Exception {
- joinAndWait(mController, DEFAULT_DATASET);
+ mController.joinAndWait(DEFAULT_DATASET);
String ifconfig = runShellCommand("ifconfig thread-wpan");
List<Inet6Address> otAddresses = mOtCtl.getAddresses();
@@ -152,46 +139,4 @@
// TODO (b/323300829): add more tests for integration with linux platform and
// ConnectivityService
-
- private static int getDeviceRole(ThreadNetworkController controller) throws Exception {
- CompletableFuture<Integer> future = new CompletableFuture<>();
- StateCallback callback = future::complete;
- controller.registerStateCallback(directExecutor(), callback);
- try {
- return future.get(CALLBACK_TIMEOUT.toMillis(), MILLISECONDS);
- } finally {
- controller.unregisterStateCallback(callback);
- }
- }
-
- private static void joinAndWait(
- ThreadNetworkController controller, ActiveOperationalDataset activeDataset)
- throws Exception {
- runAsShell(
- PERMISSION_THREAD_NETWORK_PRIVILEGED,
- () -> controller.join(activeDataset, directExecutor(), result -> {}));
- waitForStateAnyOf(controller, List.of(DEVICE_ROLE_LEADER), RESTART_JOIN_TIMEOUT);
- }
-
- private static void leaveAndWait(ThreadNetworkController controller) throws Exception {
- CompletableFuture<Void> future = new CompletableFuture<>();
- runAsShell(
- PERMISSION_THREAD_NETWORK_PRIVILEGED,
- () -> controller.leave(directExecutor(), future::complete));
- future.get(LEAVE_TIMEOUT.toMillis(), MILLISECONDS);
- }
-
- private static void setTestUpStreamNetworkAndWait(
- ThreadNetworkController controller, @Nullable String networkInterfaceName)
- throws Exception {
- CompletableFuture<Void> future = new CompletableFuture<>();
- runAsShell(
- PERMISSION_THREAD_NETWORK_PRIVILEGED,
- NETWORK_SETTINGS,
- () -> {
- controller.setTestNetworkAsUpstream(
- networkInterfaceName, directExecutor(), future::complete);
- });
- future.get(CALLBACK_TIMEOUT.toMillis(), MILLISECONDS);
- }
}
diff --git a/thread/tests/integration/src/android/net/thread/utils/ThreadNetworkControllerWrapper.java b/thread/tests/integration/src/android/net/thread/utils/ThreadNetworkControllerWrapper.java
new file mode 100644
index 0000000..e7b4cd9
--- /dev/null
+++ b/thread/tests/integration/src/android/net/thread/utils/ThreadNetworkControllerWrapper.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2024 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 android.net.thread.utils;
+
+import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.net.thread.ThreadNetworkManager.PERMISSION_THREAD_NETWORK_PRIVILEGED;
+import static android.net.thread.utils.IntegrationTestUtils.CALLBACK_TIMEOUT;
+
+import static com.android.testutils.TestPermissionUtil.runAsShell;
+
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.net.thread.ActiveOperationalDataset;
+import android.net.thread.ThreadNetworkController;
+import android.net.thread.ThreadNetworkController.StateCallback;
+import android.net.thread.ThreadNetworkException;
+import android.net.thread.ThreadNetworkManager;
+import android.os.OutcomeReceiver;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/** A helper class which provides synchronous API wrappers for {@link ThreadNetworkController}. */
+public final class ThreadNetworkControllerWrapper {
+ public static final Duration JOIN_TIMEOUT = Duration.ofSeconds(10);
+ public static final Duration LEAVE_TIMEOUT = Duration.ofSeconds(2);
+ private static final Duration CALLBACK_TIMEOUT = Duration.ofSeconds(1);
+
+ private final ThreadNetworkController mController;
+
+ /**
+ * Returns a new {@link ThreadNetworkControllerWrapper} instance or {@code null} if Thread
+ * feature is not supported on this device.
+ */
+ @Nullable
+ public static ThreadNetworkControllerWrapper newInstance(Context context) {
+ final ThreadNetworkManager manager = context.getSystemService(ThreadNetworkManager.class);
+ if (manager == null) {
+ return null;
+ }
+ return new ThreadNetworkControllerWrapper(manager.getAllThreadNetworkControllers().get(0));
+ }
+
+ private ThreadNetworkControllerWrapper(ThreadNetworkController controller) {
+ mController = controller;
+ }
+
+ /**
+ * Returns the Thread enabled state.
+ *
+ * <p>The value can be one of {@code ThreadNetworkController#STATE_*}.
+ */
+ public final int getEnabledState()
+ throws InterruptedException, ExecutionException, TimeoutException {
+ CompletableFuture<Integer> future = new CompletableFuture<>();
+ StateCallback callback =
+ new StateCallback() {
+ @Override
+ public void onThreadEnableStateChanged(int enabledState) {
+ future.complete(enabledState);
+ }
+
+ @Override
+ public void onDeviceRoleChanged(int deviceRole) {}
+ };
+
+ runAsShell(
+ ACCESS_NETWORK_STATE,
+ () -> mController.registerStateCallback(directExecutor(), callback));
+ try {
+ return future.get(CALLBACK_TIMEOUT.toSeconds(), SECONDS);
+ } finally {
+ runAsShell(ACCESS_NETWORK_STATE, () -> mController.unregisterStateCallback(callback));
+ }
+ }
+
+ /**
+ * Returns the Thread device role.
+ *
+ * <p>The value can be one of {@code ThreadNetworkController#DEVICE_ROLE_*}.
+ */
+ public final int getDeviceRole()
+ throws InterruptedException, ExecutionException, TimeoutException {
+ CompletableFuture<Integer> future = new CompletableFuture<>();
+ StateCallback callback = future::complete;
+
+ runAsShell(
+ ACCESS_NETWORK_STATE,
+ () -> mController.registerStateCallback(directExecutor(), callback));
+ try {
+ return future.get(CALLBACK_TIMEOUT.toSeconds(), SECONDS);
+ } finally {
+ runAsShell(ACCESS_NETWORK_STATE, () -> mController.unregisterStateCallback(callback));
+ }
+ }
+
+ /** Joins the given network and wait for this device to become attached. */
+ public void joinAndWait(ActiveOperationalDataset activeDataset)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ CompletableFuture<Void> future = new CompletableFuture<>();
+ runAsShell(
+ PERMISSION_THREAD_NETWORK_PRIVILEGED,
+ () ->
+ mController.join(
+ activeDataset, directExecutor(), newOutcomeReceiver(future)));
+ future.get(JOIN_TIMEOUT.toSeconds(), SECONDS);
+ }
+
+ /** An synchronous variant of {@link ThreadNetworkController#leave}. */
+ public void leaveAndWait() throws InterruptedException, ExecutionException, TimeoutException {
+ CompletableFuture<Void> future = new CompletableFuture<>();
+ runAsShell(
+ PERMISSION_THREAD_NETWORK_PRIVILEGED,
+ () -> mController.leave(directExecutor(), future::complete));
+ future.get(LEAVE_TIMEOUT.toSeconds(), SECONDS);
+ }
+
+ /** Waits for the device role to become {@code deviceRole}. */
+ public int waitForRole(int deviceRole, Duration timeout)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ return waitForRoleAnyOf(List.of(deviceRole), timeout);
+ }
+
+ /** Waits for the device role to become one of the values specified in {@code deviceRoles}. */
+ public int waitForRoleAnyOf(List<Integer> deviceRoles, Duration timeout)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ CompletableFuture<Integer> future = new CompletableFuture<>();
+ ThreadNetworkController.StateCallback callback =
+ newRole -> {
+ if (deviceRoles.contains(newRole)) {
+ future.complete(newRole);
+ }
+ };
+
+ runAsShell(
+ ACCESS_NETWORK_STATE,
+ () -> mController.registerStateCallback(directExecutor(), callback));
+
+ try {
+ return future.get(timeout.toSeconds(), SECONDS);
+ } finally {
+ mController.unregisterStateCallback(callback);
+ }
+ }
+
+ /** An synchronous variant of {@link ThreadNetworkController#setTestNetworkAsUpstream}. */
+ public void setTestNetworkAsUpstreamAndWait(@Nullable String networkInterfaceName)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ CompletableFuture<Void> future = new CompletableFuture<>();
+ runAsShell(
+ PERMISSION_THREAD_NETWORK_PRIVILEGED,
+ NETWORK_SETTINGS,
+ () -> {
+ mController.setTestNetworkAsUpstream(
+ networkInterfaceName, directExecutor(), future::complete);
+ });
+ future.get(CALLBACK_TIMEOUT.toSeconds(), SECONDS);
+ }
+
+ private static <V> OutcomeReceiver<V, ThreadNetworkException> newOutcomeReceiver(
+ CompletableFuture<V> future) {
+ return new OutcomeReceiver<V, ThreadNetworkException>() {
+ @Override
+ public void onResult(V result) {
+ future.complete(result);
+ }
+
+ @Override
+ public void onError(ThreadNetworkException e) {
+ future.completeExceptionally(e);
+ }
+ };
+ }
+}