Merge changes from topic "move-testbpfmap"
* changes:
Extract TestBpfMap from BpfCoordinatorTest.
Add jarjar rule for com.android.testutils.TestBpfMap
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index ecbaf61..ae96e8c 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -99,7 +99,6 @@
],
min_sdk_version: "30",
header_libs: [
- "bpf_syscall_wrappers",
"bpf_connectivity_headers",
],
srcs: [
@@ -112,7 +111,6 @@
static_libs: [
"libnet_utils_device_common_bpfjni",
"libnetjniutils",
- "libtcutils",
],
// We cannot use plain "libc++" here to link libc++ dynamically because it results in:
diff --git a/Tethering/apex/canned_fs_config b/Tethering/apex/canned_fs_config
index 06e9617..5a03347 100644
--- a/Tethering/apex/canned_fs_config
+++ b/Tethering/apex/canned_fs_config
@@ -1,2 +1,2 @@
-/bin/for-system 0 1000 0550
+/bin/for-system 0 1000 0750
/bin/for-system/clatd 1029 1029 06755
diff --git a/bpf_progs/bpf_shared.h b/bpf_progs/bpf_shared.h
index f0df97b..2ddc7b8 100644
--- a/bpf_progs/bpf_shared.h
+++ b/bpf_progs/bpf_shared.h
@@ -130,7 +130,8 @@
STANDBY_MATCH = (1 << 3),
POWERSAVE_MATCH = (1 << 4),
RESTRICTED_MATCH = (1 << 5),
- IIF_MATCH = (1 << 6),
+ LOW_POWER_STANDBY_MATCH = (1 << 6),
+ IIF_MATCH = (1 << 7),
};
enum BpfPermissionMatch {
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index f0af8b4..c1a74e7 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -210,6 +210,9 @@
if ((enabledRules & RESTRICTED_MATCH) && !(uidRules & RESTRICTED_MATCH)) {
return BPF_DROP;
}
+ if ((enabledRules & LOW_POWER_STANDBY_MATCH) && !(uidRules & LOW_POWER_STANDBY_MATCH)) {
+ return BPF_DROP;
+ }
}
if (direction == BPF_INGRESS && (uidRules & IIF_MATCH)) {
// Drops packets not coming from lo nor the allowlisted interface
diff --git a/framework-t/api/current.txt b/framework-t/api/current.txt
index 0443456..7977cc5 100644
--- a/framework-t/api/current.txt
+++ b/framework-t/api/current.txt
@@ -3,6 +3,8 @@
public final class NsdManager {
method public void discoverServices(String, int, android.net.nsd.NsdManager.DiscoveryListener);
+ method public void discoverServices(@NonNull String, int, @Nullable android.net.Network, @NonNull android.net.nsd.NsdManager.DiscoveryListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void discoverServices(@NonNull String, int, @NonNull android.net.NetworkRequest, @NonNull android.net.nsd.NsdManager.DiscoveryListener);
method public void registerService(android.net.nsd.NsdServiceInfo, int, android.net.nsd.NsdManager.RegistrationListener);
method public void resolveService(android.net.nsd.NsdServiceInfo, android.net.nsd.NsdManager.ResolveListener);
method public void stopServiceDiscovery(android.net.nsd.NsdManager.DiscoveryListener);
@@ -43,12 +45,14 @@
method public int describeContents();
method public java.util.Map<java.lang.String,byte[]> getAttributes();
method public java.net.InetAddress getHost();
+ method @Nullable public android.net.Network getNetwork();
method public int getPort();
method public String getServiceName();
method public String getServiceType();
method public void removeAttribute(String);
method public void setAttribute(String, String);
method public void setHost(java.net.InetAddress);
+ method public void setNetwork(@Nullable android.net.Network);
method public void setPort(int);
method public void setServiceName(String);
method public void setServiceType(String);
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 92ede91..5246623 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -941,6 +941,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
private final IConnectivityManager mService;
+ // LINT.IfChange(firewall_chain)
/**
* Firewall chain for device idle (doze mode).
* Allowlist of apps that have network access in device idle.
@@ -991,6 +992,7 @@
FIREWALL_CHAIN_LOW_POWER_STANDBY
})
public @interface FirewallChain {}
+ // LINT.ThenChange(packages/modules/Connectivity/service/native/include/Common.h)
/**
* A kludge to facilitate static access where a Context pointer isn't available, like in the
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index feb9fc1..b6cd760 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -1615,8 +1615,8 @@
* <p>
* Note that when used to register a network callback, this specifies the minimum acceptable
* signal strength. When received as the state of an existing network it specifies the current
- * value. A value of code SIGNAL_STRENGTH_UNSPECIFIED} means no value when received and has no
- * effect when requesting a callback.
+ * value. A value of {@link #SIGNAL_STRENGTH_UNSPECIFIED} means no value when received and has
+ * no effect when requesting a callback.
*
* @param signalStrength the bearer-specific signal strength.
* @hide
diff --git a/netd/Android.bp b/netd/Android.bp
index 53c1bd4..b98a859 100644
--- a/netd/Android.bp
+++ b/netd/Android.bp
@@ -31,12 +31,10 @@
"BpfHandler.cpp",
"NetdUpdatable.cpp",
],
- static_libs: [
- "libnetdutils",
- ],
shared_libs: [
"libbase",
"liblog",
+ "libnetdutils",
],
export_include_dirs: ["include"],
header_abi_checker: {
diff --git a/service/Android.bp b/service/Android.bp
index 6d187ba..8d491b3 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -33,10 +33,8 @@
srcs: [
"jni/com_android_net_module_util/onload.cpp",
],
- stl: "libc++_static",
static_libs: [
"libnet_utils_device_common_bpfjni",
- "libtcutils",
],
shared_libs: [
"liblog",
@@ -62,21 +60,20 @@
"jni/com_android_server_TestNetworkService.cpp",
"jni/onload.cpp",
],
- stl: "libc++_static",
header_libs: [
"bpf_connectivity_headers",
"libbase_headers",
],
static_libs: [
- "libbase",
"libclat",
"libip_checksum",
- "libnetdutils",
"libnetjniutils",
"libtraffic_controller",
"netd_aidl_interface-lateststable-ndk",
],
shared_libs: [
+ "libbase",
+ "libnetdutils",
"liblog",
"libnativehelper",
],
diff --git a/service/native/Android.bp b/service/native/Android.bp
index a20c54f..cb26bc3 100644
--- a/service/native/Android.bp
+++ b/service/native/Android.bp
@@ -26,11 +26,8 @@
],
header_libs: [
"bpf_connectivity_headers",
- "bpf_headers",
- "bpf_syscall_wrappers",
],
static_libs: [
- "libnetdutils",
// TrafficController would use the constants of INetd so that add
// netd_aidl_interface-lateststable-ndk.
"netd_aidl_interface-lateststable-ndk",
@@ -39,6 +36,7 @@
// TODO: Find a good way to remove libbase.
"libbase",
"libcutils",
+ "libnetdutils",
"libutils",
"liblog",
],
diff --git a/service/native/TrafficController.cpp b/service/native/TrafficController.cpp
index 51dd502..5981906 100644
--- a/service/native/TrafficController.cpp
+++ b/service/native/TrafficController.cpp
@@ -75,6 +75,7 @@
const char* TrafficController::LOCAL_STANDBY = "fw_standby";
const char* TrafficController::LOCAL_POWERSAVE = "fw_powersave";
const char* TrafficController::LOCAL_RESTRICTED = "fw_restricted";
+const char* TrafficController::LOCAL_LOW_POWER_STANDBY = "fw_low_power_standby";
static_assert(BPF_PERMISSION_INTERNET == INetd::PERMISSION_INTERNET,
"Mismatch between BPF and AIDL permissions: PERMISSION_INTERNET");
@@ -97,6 +98,7 @@
FLAG_MSG_TRANS(matchType, STANDBY_MATCH, match);
FLAG_MSG_TRANS(matchType, POWERSAVE_MATCH, match);
FLAG_MSG_TRANS(matchType, RESTRICTED_MATCH, match);
+ FLAG_MSG_TRANS(matchType, LOW_POWER_STANDBY_MATCH, match);
FLAG_MSG_TRANS(matchType, IIF_MATCH, match);
if (match) {
return StringPrintf("Unknown match: %u", match);
@@ -426,6 +428,8 @@
return ALLOWLIST;
case RESTRICTED:
return ALLOWLIST;
+ case LOW_POWER_STANDBY:
+ return ALLOWLIST;
case NONE:
default:
return DENYLIST;
@@ -448,6 +452,9 @@
case RESTRICTED:
res = updateOwnerMapEntry(RESTRICTED_MATCH, uid, rule, type);
break;
+ case LOW_POWER_STANDBY:
+ res = updateOwnerMapEntry(LOW_POWER_STANDBY_MATCH, uid, rule, type);
+ break;
case NONE:
default:
ALOGW("Unknown child chain: %d", chain);
@@ -526,6 +533,8 @@
res = replaceRulesInMap(POWERSAVE_MATCH, uids);
} else if (!name.compare(LOCAL_RESTRICTED)) {
res = replaceRulesInMap(RESTRICTED_MATCH, uids);
+ } else if (!name.compare(LOCAL_LOW_POWER_STANDBY)) {
+ res = replaceRulesInMap(LOW_POWER_STANDBY_MATCH, uids);
} else {
ALOGE("unknown chain name: %s", name.c_str());
return -EINVAL;
@@ -562,6 +571,9 @@
case RESTRICTED:
match = RESTRICTED_MATCH;
break;
+ case LOW_POWER_STANDBY:
+ match = LOW_POWER_STANDBY_MATCH;
+ break;
default:
return -EINVAL;
}
diff --git a/service/native/TrafficControllerTest.cpp b/service/native/TrafficControllerTest.cpp
index 39f3365..d0eca34 100644
--- a/service/native/TrafficControllerTest.cpp
+++ b/service/native/TrafficControllerTest.cpp
@@ -470,6 +470,7 @@
checkUidOwnerRuleForChain(STANDBY, STANDBY_MATCH);
checkUidOwnerRuleForChain(POWERSAVE, POWERSAVE_MATCH);
checkUidOwnerRuleForChain(RESTRICTED, RESTRICTED_MATCH);
+ checkUidOwnerRuleForChain(LOW_POWER_STANDBY, LOW_POWER_STANDBY_MATCH);
ASSERT_EQ(-EINVAL, mTc.changeUidOwnerRule(NONE, TEST_UID, ALLOW, ALLOWLIST));
ASSERT_EQ(-EINVAL, mTc.changeUidOwnerRule(INVALID_CHAIN, TEST_UID, ALLOW, ALLOWLIST));
}
@@ -480,6 +481,7 @@
checkUidMapReplace("fw_standby", uids, STANDBY_MATCH);
checkUidMapReplace("fw_powersave", uids, POWERSAVE_MATCH);
checkUidMapReplace("fw_restricted", uids, RESTRICTED_MATCH);
+ checkUidMapReplace("fw_low_power_standby", uids, LOW_POWER_STANDBY_MATCH);
ASSERT_EQ(-EINVAL, mTc.replaceUidOwnerMap("unknow", true, uids));
}
diff --git a/service/native/include/Common.h b/service/native/include/Common.h
index 7c0b797..dc44845 100644
--- a/service/native/include/Common.h
+++ b/service/native/include/Common.h
@@ -27,11 +27,14 @@
enum FirewallType { ALLOWLIST = INetd::FIREWALL_ALLOWLIST, DENYLIST = INetd::FIREWALL_DENYLIST };
+// LINT.IfChange(firewall_chain)
enum ChildChain {
- NONE = INetd::FIREWALL_CHAIN_NONE,
- DOZABLE = INetd::FIREWALL_CHAIN_DOZABLE,
- STANDBY = INetd::FIREWALL_CHAIN_STANDBY,
- POWERSAVE = INetd::FIREWALL_CHAIN_POWERSAVE,
- RESTRICTED = INetd::FIREWALL_CHAIN_RESTRICTED,
+ NONE = 0,
+ DOZABLE = 1,
+ STANDBY = 2,
+ POWERSAVE = 3,
+ RESTRICTED = 4,
+ LOW_POWER_STANDBY = 5,
INVALID_CHAIN
};
+// LINT.ThenChange(packages/modules/Connectivity/framework/src/android/net/ConnectivityManager.java)
diff --git a/service/native/include/TrafficController.h b/service/native/include/TrafficController.h
index ddcf445..e741dd6 100644
--- a/service/native/include/TrafficController.h
+++ b/service/native/include/TrafficController.h
@@ -99,6 +99,7 @@
static const char* LOCAL_STANDBY;
static const char* LOCAL_POWERSAVE;
static const char* LOCAL_RESTRICTED;
+ static const char* LOCAL_LOW_POWER_STANDBY;
private:
/*
@@ -160,7 +161,7 @@
* the map right now:
* - Entry with UID_RULES_CONFIGURATION_KEY:
* Store the configuration for the current uid rules. It indicates the device
- * is in doze/powersave/standby/restricted mode.
+ * is in doze/powersave/standby/restricted/low power standby mode.
* - Entry with CURRENT_STATS_MAP_CONFIGURATION_KEY:
* Stores the current live stats map that kernel program is writing to.
* Userspace can do scraping and cleaning job on the other one depending on the
diff --git a/service/native/libs/libclat/Android.bp b/service/native/libs/libclat/Android.bp
index 5e208d8..17ee996 100644
--- a/service/native/libs/libclat/Android.bp
+++ b/service/native/libs/libclat/Android.bp
@@ -21,8 +21,7 @@
defaults: ["netd_defaults"],
header_libs: [
"bpf_connectivity_headers",
- "bpf_headers",
- "bpf_syscall_wrappers",
+ "libbase_headers",
],
srcs: [
"TcUtils.cpp", // TODO: move to frameworks/libs/net
@@ -31,7 +30,6 @@
],
stl: "libc++_static",
static_libs: [
- "libbase",
"libip_checksum",
"libnetdutils", // for netdutils/UidConstants.h in bpf_shared.h
],
@@ -47,8 +45,6 @@
test_suites: ["device-tests"],
header_libs: [
"bpf_connectivity_headers",
- "bpf_headers",
- "bpf_syscall_wrappers",
],
srcs: [
"TcUtilsTest.cpp",
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index a9a06e4..198190a 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -10912,6 +10912,10 @@
case ConnectivityManager.FIREWALL_CHAIN_RESTRICTED:
mBpfNetMaps.replaceUidChain("fw_restricted", true /* isAllowList */, uids);
break;
+ case ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY:
+ mBpfNetMaps.replaceUidChain("fw_low_power_standby", true /* isAllowList */,
+ uids);
+ break;
default:
throw new IllegalArgumentException("replaceFirewallChain with invalid chain: "
+ chain);
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 23814c9..9506081 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -15,6 +15,19 @@
*/
package android.net.cts
+import android.Manifest.permission.MANAGE_TEST_NETWORKS
+import android.net.ConnectivityManager
+import android.net.ConnectivityManager.NetworkCallback
+import android.net.LinkProperties
+import android.net.Network
+import android.net.NetworkAgentConfig
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
+import android.net.NetworkCapabilities.TRANSPORT_TEST
+import android.net.NetworkRequest
+import android.net.TestNetworkInterface
+import android.net.TestNetworkManager
+import android.net.TestNetworkSpecifier
import android.net.cts.NsdManagerTest.NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStarted
import android.net.cts.NsdManagerTest.NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStopped
import android.net.cts.NsdManagerTest.NsdDiscoveryRecord.DiscoveryEvent.ServiceFound
@@ -32,14 +45,27 @@
import android.net.nsd.NsdManager.RegistrationListener
import android.net.nsd.NsdManager.ResolveListener
import android.net.nsd.NsdServiceInfo
+import android.os.HandlerThread
import android.platform.test.annotations.AppModeFull
import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
import com.android.net.module.util.ArrayTrackRecord
import com.android.net.module.util.TrackRecord
+import com.android.networkstack.apishim.ConstantsShim
+import com.android.networkstack.apishim.NsdShimImpl
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.SC_V2
+import com.android.testutils.TestableNetworkAgent
+import com.android.testutils.TestableNetworkCallback
+import com.android.testutils.runAsShell
+import com.android.testutils.tryTest
+import org.junit.After
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertTrue
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.net.ServerSocket
@@ -57,12 +83,37 @@
private const val TIMEOUT_MS = 2000L
private const val DBG = false
+private val nsdShim = NsdShimImpl.newInstance()
+
@AppModeFull(reason = "Socket cannot bind in instant app mode")
@RunWith(AndroidJUnit4::class)
class NsdManagerTest {
+ // NsdManager is not updatable before S, so tests do not need to be backwards compatible
+ @get:Rule
+ val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = SC_V2)
+
private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
private val nsdManager by lazy { context.getSystemService(NsdManager::class.java) }
+
+ private val cm by lazy { context.getSystemService(ConnectivityManager::class.java) }
private val serviceName = "NsdTest%09d".format(Random().nextInt(1_000_000_000))
+ private val handlerThread = HandlerThread(NsdManagerTest::class.java.simpleName)
+
+ private lateinit var testNetwork1: TestTapNetwork
+ private lateinit var testNetwork2: TestTapNetwork
+
+ private class TestTapNetwork(
+ val iface: TestNetworkInterface,
+ val requestCb: NetworkCallback,
+ val agent: TestableNetworkAgent,
+ val network: Network
+ ) {
+ fun close(cm: ConnectivityManager) {
+ cm.unregisterNetworkCallback(requestCb)
+ agent.unregister()
+ iface.fileDescriptor.close()
+ }
+ }
private interface NsdEvent
private open class NsdRecord<T : NsdEvent> private constructor(
@@ -163,9 +214,14 @@
add(ServiceLost(si))
}
- fun waitForServiceDiscovered(serviceName: String): NsdServiceInfo {
+ fun waitForServiceDiscovered(
+ serviceName: String,
+ expectedNetwork: Network? = null
+ ): NsdServiceInfo {
return expectCallbackEventually<ServiceFound> {
- it.serviceInfo.serviceName == serviceName
+ it.serviceInfo.serviceName == serviceName &&
+ (expectedNetwork == null ||
+ expectedNetwork == nsdShim.getNetwork(it.serviceInfo))
}.serviceInfo
}
}
@@ -188,6 +244,58 @@
}
}
+ @Before
+ fun setUp() {
+ handlerThread.start()
+
+ runAsShell(MANAGE_TEST_NETWORKS) {
+ testNetwork1 = createTestNetwork()
+ testNetwork2 = createTestNetwork()
+ }
+ }
+
+ private fun createTestNetwork(): TestTapNetwork {
+ val tnm = context.getSystemService(TestNetworkManager::class.java)
+ val iface = tnm.createTapInterface()
+ val cb = TestableNetworkCallback()
+ val testNetworkSpecifier = TestNetworkSpecifier(iface.interfaceName)
+ cm.requestNetwork(NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_TRUSTED)
+ .addTransportType(TRANSPORT_TEST)
+ .setNetworkSpecifier(testNetworkSpecifier)
+ .build(), cb)
+ val agent = registerTestNetworkAgent(iface.interfaceName)
+ val network = agent.network ?: fail("Registered agent should have a network")
+ // The network has no INTERNET capability, so will be marked validated immediately
+ cb.expectAvailableThenValidatedCallbacks(network)
+ return TestTapNetwork(iface, cb, agent, network)
+ }
+
+ private fun registerTestNetworkAgent(ifaceName: String): TestableNetworkAgent {
+ val agent = TestableNetworkAgent(context, handlerThread.looper,
+ NetworkCapabilities().apply {
+ removeCapability(NET_CAPABILITY_TRUSTED)
+ addTransportType(TRANSPORT_TEST)
+ setNetworkSpecifier(TestNetworkSpecifier(ifaceName))
+ },
+ LinkProperties().apply {
+ interfaceName = ifaceName
+ },
+ NetworkAgentConfig.Builder().build())
+ agent.register()
+ agent.markConnected()
+ return agent
+ }
+
+ @After
+ fun tearDown() {
+ runAsShell(MANAGE_TEST_NETWORKS) {
+ testNetwork1.close(cm)
+ testNetwork2.close(cm)
+ }
+ handlerThread.quitSafely()
+ }
+
@Test
fun testNsdManager() {
val si = NsdServiceInfo()
@@ -298,6 +406,149 @@
registrationRecord2.expectCallback<ServiceUnregistered>()
}
+ @Test
+ fun testNsdManager_DiscoverOnNetwork() {
+ // This tests requires shims supporting T+ APIs (discovering on specific network)
+ assumeTrue(ConstantsShim.VERSION > SC_V2)
+
+ val si = NsdServiceInfo()
+ si.serviceType = SERVICE_TYPE
+ si.serviceName = this.serviceName
+ si.port = 12345 // Test won't try to connect so port does not matter
+
+ val registrationRecord = NsdRegistrationRecord()
+ val registeredInfo = registerService(registrationRecord, si)
+
+ tryTest {
+ val discoveryRecord = NsdDiscoveryRecord()
+ nsdShim.discoverServices(nsdManager, SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
+ testNetwork1.network, discoveryRecord)
+
+ val foundInfo = discoveryRecord.waitForServiceDiscovered(
+ serviceName, testNetwork1.network)
+ assertEquals(testNetwork1.network, nsdShim.getNetwork(foundInfo))
+
+ // Rewind to ensure the service is not found on the other interface
+ discoveryRecord.nextEvents.rewind(0)
+ assertNull(discoveryRecord.nextEvents.poll(timeoutMs = 100L) {
+ it is ServiceFound &&
+ it.serviceInfo.serviceName == registeredInfo.serviceName &&
+ nsdShim.getNetwork(it.serviceInfo) != testNetwork1.network
+ }, "The service should not be found on this network")
+ } cleanup {
+ nsdManager.unregisterService(registrationRecord)
+ }
+ }
+
+ @Test
+ fun testNsdManager_DiscoverWithNetworkRequest() {
+ // This tests requires shims supporting T+ APIs (discovering on network request)
+ assumeTrue(ConstantsShim.VERSION > SC_V2)
+
+ val si = NsdServiceInfo()
+ si.serviceType = SERVICE_TYPE
+ si.serviceName = this.serviceName
+ si.port = 12345 // Test won't try to connect so port does not matter
+
+ val registrationRecord = NsdRegistrationRecord()
+ val registeredInfo1 = registerService(registrationRecord, si)
+ val discoveryRecord = NsdDiscoveryRecord()
+
+ tryTest {
+ val specifier = TestNetworkSpecifier(testNetwork1.iface.interfaceName)
+ nsdShim.discoverServices(nsdManager, SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
+ NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_TRUSTED)
+ .addTransportType(TRANSPORT_TEST)
+ .setNetworkSpecifier(specifier)
+ .build(),
+ discoveryRecord)
+
+ val discoveryStarted = discoveryRecord.expectCallback<DiscoveryStarted>()
+ assertEquals(SERVICE_TYPE, discoveryStarted.serviceType)
+
+ val serviceDiscovered = discoveryRecord.expectCallback<ServiceFound>()
+ assertEquals(registeredInfo1.serviceName, serviceDiscovered.serviceInfo.serviceName)
+ assertEquals(testNetwork1.network, nsdShim.getNetwork(serviceDiscovered.serviceInfo))
+
+ // Unregister, then register the service back: it should be lost and found again
+ nsdManager.unregisterService(registrationRecord)
+ val serviceLost1 = discoveryRecord.expectCallback<ServiceLost>()
+ assertEquals(registeredInfo1.serviceName, serviceLost1.serviceInfo.serviceName)
+ assertEquals(testNetwork1.network, nsdShim.getNetwork(serviceLost1.serviceInfo))
+
+ registrationRecord.expectCallback<ServiceUnregistered>()
+ val registeredInfo2 = registerService(registrationRecord, si)
+ val serviceDiscovered2 = discoveryRecord.expectCallback<ServiceFound>()
+ assertEquals(registeredInfo2.serviceName, serviceDiscovered2.serviceInfo.serviceName)
+ assertEquals(testNetwork1.network, nsdShim.getNetwork(serviceDiscovered2.serviceInfo))
+
+ // Teardown, then bring back up a network on the test interface: the service should
+ // go away, then come back
+ testNetwork1.agent.unregister()
+ val serviceLost = discoveryRecord.expectCallback<ServiceLost>()
+ assertEquals(registeredInfo2.serviceName, serviceLost.serviceInfo.serviceName)
+ assertEquals(testNetwork1.network, nsdShim.getNetwork(serviceLost.serviceInfo))
+
+ val newAgent = runAsShell(MANAGE_TEST_NETWORKS) {
+ registerTestNetworkAgent(testNetwork1.iface.interfaceName)
+ }
+ val newNetwork = newAgent.network ?: fail("Registered agent should have a network")
+ val serviceDiscovered3 = discoveryRecord.expectCallback<ServiceFound>()
+ assertEquals(registeredInfo2.serviceName, serviceDiscovered3.serviceInfo.serviceName)
+ assertEquals(newNetwork, nsdShim.getNetwork(serviceDiscovered3.serviceInfo))
+ } cleanupStep {
+ nsdManager.stopServiceDiscovery(discoveryRecord)
+ discoveryRecord.expectCallback<DiscoveryStopped>()
+ } cleanup {
+ nsdManager.unregisterService(registrationRecord)
+ }
+ }
+
+ @Test
+ fun testNsdManager_ResolveOnNetwork() {
+ // This tests requires shims supporting T+ APIs (NsdServiceInfo.network)
+ assumeTrue(ConstantsShim.VERSION > SC_V2)
+
+ val si = NsdServiceInfo()
+ si.serviceType = SERVICE_TYPE
+ si.serviceName = this.serviceName
+ si.port = 12345 // Test won't try to connect so port does not matter
+
+ val registrationRecord = NsdRegistrationRecord()
+ val registeredInfo = registerService(registrationRecord, si)
+ tryTest {
+ val resolveRecord = NsdResolveRecord()
+
+ val discoveryRecord = NsdDiscoveryRecord()
+ nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
+
+ val foundInfo1 = discoveryRecord.waitForServiceDiscovered(
+ serviceName, testNetwork1.network)
+ assertEquals(testNetwork1.network, nsdShim.getNetwork(foundInfo1))
+ // Rewind as the service could be found on each interface in any order
+ discoveryRecord.nextEvents.rewind(0)
+ val foundInfo2 = discoveryRecord.waitForServiceDiscovered(
+ serviceName, testNetwork2.network)
+ assertEquals(testNetwork2.network, nsdShim.getNetwork(foundInfo2))
+
+ nsdManager.resolveService(foundInfo1, resolveRecord)
+ val cb = resolveRecord.expectCallback<ServiceResolved>()
+ cb.serviceInfo.let {
+ // Resolved service type has leading dot
+ assertEquals(".$SERVICE_TYPE", it.serviceType)
+ assertEquals(registeredInfo.serviceName, it.serviceName)
+ assertEquals(si.port, it.port)
+ assertEquals(testNetwork1.network, nsdShim.getNetwork(it))
+ }
+ // TODO: check that MDNS packets are sent only on testNetwork1.
+ } cleanupStep {
+ nsdManager.unregisterService(registrationRecord)
+ } cleanup {
+ registrationRecord.expectCallback<ServiceUnregistered>()
+ }
+ }
+
/**
* Register a service and return its registration record.
*/
diff --git a/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java b/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
index ca8cf07..e5e7ebc 100644
--- a/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
+++ b/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.net.Network;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
@@ -123,6 +124,7 @@
fullInfo.setServiceType("_kitten._tcp");
fullInfo.setPort(4242);
fullInfo.setHost(LOCALHOST);
+ fullInfo.setNetwork(new Network(123));
checkParcelable(fullInfo);
NsdServiceInfo noHostInfo = new NsdServiceInfo();
@@ -172,6 +174,7 @@
assertEquals(original.getServiceType(), result.getServiceType());
assertEquals(original.getHost(), result.getHost());
assertTrue(original.getPort() == result.getPort());
+ assertEquals(original.getNetwork(), result.getNetwork());
// Assert equality of attribute map.
Map<String, byte[]> originalMap = original.getAttributes();
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 6d1d765..5086943 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -218,14 +218,14 @@
client.discoverServices("a_type", PROTOCOL, listener2);
waitForIdle();
verify(mDaemon, times(1)).maybeStart();
- verifyDaemonCommand("discover 3 a_type");
+ verifyDaemonCommand("discover 3 a_type 0");
// Client resolve request
NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class);
client.resolveService(request, listener3);
waitForIdle();
verify(mDaemon, times(1)).maybeStart();
- verifyDaemonCommand("resolve 4 a_name a_type local.");
+ verifyDaemonCommand("resolve 4 a_name a_type local. 0");
// Client disconnects, stop the daemon after CLEANUP_DELAY_MS.
deathRecipient.binderDied();