Add flags for allowlisting types for new NSD impl

The flags allow enabling MdnsDiscoveryManager or MdnsAdvertiser for
specific service types only. For example:

  mdns_type_allowlist_flags = "_type1._tcp:flag1,_type2._tcp:flag2"
  mdns_discovery_manager_allowlist_flag1_version = 1234
  mdns_advertiser_allowlist_flag2_version = 2345

will enable MdnsDiscoveryManager when discovering/resolving services of
type _type1._tcp, and MdnsAdvertiser when advertising services of type
_type2._tcp.

Test: atest NsdServiceTest
Bug: 270885892
Change-Id: I75c31a28472210bf8777409ea7aff1e3d8bf0a0d
This commit is contained in:
Remi NGUYEN VAN
2023-03-03 17:50:50 +09:00
parent de5adc4f1f
commit 151d0a5528
2 changed files with 142 additions and 9 deletions

View File

@@ -20,6 +20,7 @@ import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.nsd.NsdManager.MDNS_DISCOVERY_MANAGER_EVENT;
import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -110,6 +111,34 @@ public class NsdService extends INsdManager.Stub {
*/
private static final String MDNS_ADVERTISER_VERSION = "mdns_advertiser_version";
/**
* Comma-separated list of type:flag mappings indicating the flags to use to allowlist
* discovery/advertising using MdnsDiscoveryManager / MdnsAdvertiser for a given type.
*
* For example _mytype._tcp.local and _othertype._tcp.local would be configured with:
* _mytype._tcp:mytype,_othertype._tcp.local:othertype
*
* In which case the flags:
* "mdns_discovery_manager_allowlist_mytype_version",
* "mdns_advertiser_allowlist_mytype_version",
* "mdns_discovery_manager_allowlist_othertype_version",
* "mdns_advertiser_allowlist_othertype_version"
* would be used to toggle MdnsDiscoveryManager / MdnsAdvertiser for each type. The flags will
* be read with
* {@link DeviceConfigUtils#isFeatureEnabled(Context, String, String, String, boolean)}.
*
* @see #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX
* @see #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX
* @see #MDNS_ALLOWLIST_FLAG_SUFFIX
*/
private static final String MDNS_TYPE_ALLOWLIST_FLAGS = "mdns_type_allowlist_flags";
private static final String MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX =
"mdns_discovery_manager_allowlist_";
private static final String MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX =
"mdns_advertiser_allowlist_";
private static final String MDNS_ALLOWLIST_FLAG_SUFFIX = "_version";
public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static final long CLEANUP_DELAY_MS = 10000;
private static final int IFACE_IDX_ANY = 0;
@@ -572,8 +601,9 @@ public class NsdService extends INsdManager.Stub {
final NsdServiceInfo info = args.serviceInfo;
id = getUniqueId();
if (mDeps.isMdnsDiscoveryManagerEnabled(mContext)) {
final String serviceType = constructServiceType(info.getServiceType());
if (mDeps.isMdnsDiscoveryManagerEnabled(mContext)
|| useDiscoveryManagerForType(serviceType)) {
if (serviceType == null) {
clientInfo.onDiscoverServicesFailed(clientId,
NsdManager.FAILURE_INTERNAL_ERROR);
@@ -667,10 +697,11 @@ public class NsdService extends INsdManager.Stub {
}
id = getUniqueId();
if (mDeps.isMdnsAdvertiserEnabled(mContext)) {
final NsdServiceInfo serviceInfo = args.serviceInfo;
final String serviceType = serviceInfo.getServiceType();
final String registerServiceType = constructServiceType(serviceType);
if (mDeps.isMdnsAdvertiserEnabled(mContext)
|| useAdvertiserForType(registerServiceType)) {
if (registerServiceType == null) {
Log.e(TAG, "Invalid service type: " + serviceType);
clientInfo.onRegisterServiceFailed(clientId,
@@ -686,7 +717,7 @@ public class NsdService extends INsdManager.Stub {
storeAdvertiserRequestMap(clientId, id, clientInfo);
} else {
maybeStartDaemon();
if (registerService(id, args.serviceInfo)) {
if (registerService(id, serviceInfo)) {
if (DBG) Log.d(TAG, "Register " + clientId + " " + id);
storeLegacyRequestMap(clientId, id, clientInfo, msg.what);
// Return success after mDns reports success
@@ -748,8 +779,9 @@ public class NsdService extends INsdManager.Stub {
final NsdServiceInfo info = args.serviceInfo;
id = getUniqueId();
if (mDeps.isMdnsDiscoveryManagerEnabled(mContext)) {
final String serviceType = constructServiceType(info.getServiceType());
if (mDeps.isMdnsDiscoveryManagerEnabled(mContext)
|| useDiscoveryManagerForType(serviceType)) {
if (serviceType == null) {
clientInfo.onResolveServiceFailed(clientId,
NsdManager.FAILURE_INTERNAL_ERROR);
@@ -1280,6 +1312,24 @@ public class NsdService extends INsdManager.Stub {
MDNS_ADVERTISER_VERSION, false /* defaultEnabled */);
}
/**
* Get the type allowlist flag value.
* @see #MDNS_TYPE_ALLOWLIST_FLAGS
*/
@Nullable
public String getTypeAllowlistFlags() {
return DeviceConfigUtils.getDeviceConfigProperty(NAMESPACE_TETHERING,
MDNS_TYPE_ALLOWLIST_FLAGS, null);
}
/**
* @see DeviceConfigUtils#isFeatureEnabled(Context, String, String, String, boolean)
*/
public boolean isFeatureEnabled(Context context, String feature) {
return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_TETHERING,
feature, DeviceConfigUtils.TETHERING_MODULE_NAME, false /* defaultEnabled */);
}
/**
* @see MdnsDiscoveryManager
*/
@@ -1305,6 +1355,41 @@ public class NsdService extends INsdManager.Stub {
}
}
/**
* Return whether a type is allowlisted to use the Java backend.
* @param type The service type
* @param flagPrefix One of {@link #MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX} or
* {@link #MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX}.
*/
private boolean isTypeAllowlistedForJavaBackend(@Nullable String type,
@NonNull String flagPrefix) {
if (type == null) return false;
final String typesConfig = mDeps.getTypeAllowlistFlags();
if (TextUtils.isEmpty(typesConfig)) return false;
final String mappingPrefix = type + ":";
String mappedFlag = null;
for (String mapping : TextUtils.split(typesConfig, ",")) {
if (mapping.startsWith(mappingPrefix)) {
mappedFlag = mapping.substring(mappingPrefix.length());
break;
}
}
if (mappedFlag == null) return false;
return mDeps.isFeatureEnabled(mContext,
flagPrefix + mappedFlag + MDNS_ALLOWLIST_FLAG_SUFFIX);
}
private boolean useDiscoveryManagerForType(@Nullable String type) {
return isTypeAllowlistedForJavaBackend(type, MDNS_DISCOVERY_MANAGER_ALLOWLIST_FLAG_PREFIX);
}
private boolean useAdvertiserForType(@Nullable String type) {
return isTypeAllowlistedForJavaBackend(type, MDNS_ADVERTISER_ALLOWLIST_FLAG_PREFIX);
}
public static NsdService create(Context context) {
HandlerThread thread = new HandlerThread(TAG);
thread.start();

View File

@@ -1056,6 +1056,54 @@ public class NsdServiceTest {
verify(mAdvertiser).removeService(serviceIdCaptor.getValue());
}
@Test
public void testTypeSpecificFeatureFlagging() {
doReturn("_type1._tcp:flag1,_type2._tcp:flag2").when(mDeps).getTypeAllowlistFlags();
doReturn(true).when(mDeps).isFeatureEnabled(any(),
eq("mdns_discovery_manager_allowlist_flag1_version"));
doReturn(true).when(mDeps).isFeatureEnabled(any(),
eq("mdns_advertiser_allowlist_flag2_version"));
final NsdManager client = connectClient(mService);
final NsdServiceInfo service1 = new NsdServiceInfo(SERVICE_NAME, "_type1._tcp");
service1.setHostAddresses(List.of(parseNumericAddress("2001:db8::123")));
service1.setPort(1234);
final NsdServiceInfo service2 = new NsdServiceInfo(SERVICE_NAME, "_type2._tcp");
service2.setHostAddresses(List.of(parseNumericAddress("2001:db8::123")));
service2.setPort(1234);
client.discoverServices(service1.getServiceType(),
NsdManager.PROTOCOL_DNS_SD, mock(DiscoveryListener.class));
client.discoverServices(service2.getServiceType(),
NsdManager.PROTOCOL_DNS_SD, mock(DiscoveryListener.class));
waitForIdle();
// The DiscoveryManager is enabled for _type1 but not _type2
verify(mDiscoveryManager).registerListener(eq("_type1._tcp.local"), any(), any());
verify(mDiscoveryManager, never()).registerListener(
eq("_type2._tcp.local"), any(), any());
client.resolveService(service1, mock(ResolveListener.class));
client.resolveService(service2, mock(ResolveListener.class));
waitForIdle();
// Same behavior for resolve
verify(mDiscoveryManager, times(2)).registerListener(
eq("_type1._tcp.local"), any(), any());
verify(mDiscoveryManager, never()).registerListener(
eq("_type2._tcp.local"), any(), any());
client.registerService(service1, NsdManager.PROTOCOL_DNS_SD,
mock(RegistrationListener.class));
client.registerService(service2, NsdManager.PROTOCOL_DNS_SD,
mock(RegistrationListener.class));
waitForIdle();
// The advertiser is enabled for _type2 but not _type1
verify(mAdvertiser, never()).addService(anyInt(), argThat(info -> matches(info, service1)));
verify(mAdvertiser).addService(anyInt(), argThat(info -> matches(info, service2)));
}
@Test
public void testAdvertiseWithMdnsAdvertiser() {
setMdnsAdvertiserEnabled();