diff --git a/framework-t/src/android/net/nsd/INsdManager.aidl b/framework-t/src/android/net/nsd/INsdManager.aidl index 89e9cdbd44..9d14b1a1b9 100644 --- a/framework-t/src/android/net/nsd/INsdManager.aidl +++ b/framework-t/src/android/net/nsd/INsdManager.aidl @@ -26,5 +26,5 @@ import android.os.Messenger; * {@hide} */ interface INsdManager { - INsdServiceConnector connect(INsdManagerCallback cb); + INsdServiceConnector connect(INsdManagerCallback cb, boolean useJavaBackend); } diff --git a/framework-t/src/android/net/nsd/NsdManager.java b/framework-t/src/android/net/nsd/NsdManager.java index 36808cfc21..96f2f8040b 100644 --- a/framework-t/src/android/net/nsd/NsdManager.java +++ b/framework-t/src/android/net/nsd/NsdManager.java @@ -16,6 +16,7 @@ package android.net.nsd; +import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND; import static android.net.connectivity.ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER; import android.annotation.IntDef; @@ -510,7 +511,8 @@ public final class NsdManager { mHandler = new ServiceHandler(t.getLooper()); try { - mService = service.connect(new NsdCallbackImpl(mHandler)); + mService = service.connect(new NsdCallbackImpl(mHandler), CompatChanges.isChangeEnabled( + ENABLE_PLATFORM_MDNS_BACKEND)); } catch (RemoteException e) { throw new RuntimeException("Failed to connect to NsdService"); } diff --git a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java index 2cfda9eda6..dfe5867906 100644 --- a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java +++ b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java @@ -73,6 +73,17 @@ public final class ConnectivityCompatChanges { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) public static final long ENABLE_SELF_CERTIFIED_CAPABILITIES_DECLARATION = 266524688; + /** + * Apps targeting < Android 14 use a legacy NSD backend. + * + * The legacy apps use a legacy native daemon as NsdManager backend, but other apps use a + * platform-integrated mDNS implementation as backend. + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + public static final long ENABLE_PLATFORM_MDNS_BACKEND = 270306772L; private ConnectivityCompatChanges() { } } diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java index c92e9a941b..cbe6691d8c 100644 --- a/service-t/src/com/android/server/NsdService.java +++ b/service-t/src/com/android/server/NsdService.java @@ -53,7 +53,6 @@ import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; -import android.util.Pair; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; @@ -397,13 +396,12 @@ public class NsdService extends INsdManager.Stub { final int clientId = msg.arg2; switch (msg.what) { case NsdManager.REGISTER_CLIENT: - final Pair arg = - (Pair) msg.obj; - final INsdManagerCallback cb = arg.second; + final ConnectorArgs arg = (ConnectorArgs) msg.obj; + final INsdManagerCallback cb = arg.callback; try { - cb.asBinder().linkToDeath(arg.first, 0); - cInfo = new ClientInfo(cb); - mClients.put(arg.first, cInfo); + cb.asBinder().linkToDeath(arg.connector, 0); + cInfo = new ClientInfo(cb, arg.useJavaBackend); + mClients.put(arg.connector, cInfo); } catch (RemoteException e) { Log.w(TAG, "Client " + clientId + " has already died"); } @@ -608,7 +606,8 @@ public class NsdService extends INsdManager.Stub { final NsdServiceInfo info = args.serviceInfo; id = getUniqueId(); final String serviceType = constructServiceType(info.getServiceType()); - if (mDeps.isMdnsDiscoveryManagerEnabled(mContext) + if (clientInfo.mUseJavaBackend + || mDeps.isMdnsDiscoveryManagerEnabled(mContext) || useDiscoveryManagerForType(serviceType)) { if (serviceType == null) { clientInfo.onDiscoverServicesFailed(clientId, @@ -702,7 +701,8 @@ public class NsdService extends INsdManager.Stub { final NsdServiceInfo serviceInfo = args.serviceInfo; final String serviceType = serviceInfo.getServiceType(); final String registerServiceType = constructServiceType(serviceType); - if (mDeps.isMdnsAdvertiserEnabled(mContext) + if (clientInfo.mUseJavaBackend + || mDeps.isMdnsAdvertiserEnabled(mContext) || useAdvertiserForType(registerServiceType)) { if (registerServiceType == null) { Log.e(TAG, "Invalid service type: " + serviceType); @@ -782,7 +782,8 @@ public class NsdService extends INsdManager.Stub { final NsdServiceInfo info = args.serviceInfo; id = getUniqueId(); final String serviceType = constructServiceType(info.getServiceType()); - if (mDeps.isMdnsDiscoveryManagerEnabled(mContext) + if (clientInfo.mUseJavaBackend + || mDeps.isMdnsDiscoveryManagerEnabled(mContext) || useDiscoveryManagerForType(serviceType)) { if (serviceType == null) { clientInfo.onResolveServiceFailed(clientId, @@ -1532,12 +1533,27 @@ public class NsdService extends INsdManager.Stub { } } + private static class ConnectorArgs { + @NonNull public final NsdServiceConnector connector; + @NonNull public final INsdManagerCallback callback; + public final boolean useJavaBackend; + + ConnectorArgs(@NonNull NsdServiceConnector connector, @NonNull INsdManagerCallback callback, + boolean useJavaBackend) { + this.connector = connector; + this.callback = callback; + this.useJavaBackend = useJavaBackend; + } + } + @Override - public INsdServiceConnector connect(INsdManagerCallback cb) { + public INsdServiceConnector connect(INsdManagerCallback cb, boolean useJavaBackend) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService"); + if (DBG) Log.d(TAG, "New client connect. useJavaBackend=" + useJavaBackend); final INsdServiceConnector connector = new NsdServiceConnector(); mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( - NsdManager.REGISTER_CLIENT, new Pair<>(connector, cb))); + NsdManager.REGISTER_CLIENT, + new ConnectorArgs((NsdServiceConnector) connector, cb, useJavaBackend))); return connector; } @@ -1793,9 +1809,12 @@ public class NsdService extends INsdManager.Stub { // The target SDK of this client < Build.VERSION_CODES.S private boolean mIsPreSClient = false; + // The flag of using java backend if the client's target SDK >= U + private final boolean mUseJavaBackend; - private ClientInfo(INsdManagerCallback cb) { + private ClientInfo(INsdManagerCallback cb, boolean useJavaBackend) { mCb = cb; + mUseJavaBackend = useJavaBackend; if (DBG) Log.d(TAG, "New client"); } diff --git a/tests/unit/java/android/net/nsd/NsdManagerTest.java b/tests/unit/java/android/net/nsd/NsdManagerTest.java index da65b62193..0965193c31 100644 --- a/tests/unit/java/android/net/nsd/NsdManagerTest.java +++ b/tests/unit/java/android/net/nsd/NsdManagerTest.java @@ -21,6 +21,7 @@ import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -73,11 +74,11 @@ public class NsdManagerTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - doReturn(mServiceConn).when(mService).connect(any()); + doReturn(mServiceConn).when(mService).connect(any(), anyBoolean()); mManager = new NsdManager(mContext, mService); final ArgumentCaptor cbCaptor = ArgumentCaptor.forClass( INsdManagerCallback.class); - verify(mService).connect(cbCaptor.capture()); + verify(mService).connect(cbCaptor.capture(), anyBoolean()); mCallback = cbCaptor.getValue(); } diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java index 0b48e08dc2..2ed989eff0 100644 --- a/tests/unit/java/com/android/server/NsdServiceTest.java +++ b/tests/unit/java/com/android/server/NsdServiceTest.java @@ -17,6 +17,8 @@ package com.android.server; import static android.net.InetAddresses.parseNumericAddress; +import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND; +import static android.net.connectivity.ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER; import static android.net.nsd.NsdManager.FAILURE_BAD_PARAMETERS; import static android.net.nsd.NsdManager.FAILURE_INTERNAL_ERROR; import static android.net.nsd.NsdManager.FAILURE_OPERATION_NOT_RUNNING; @@ -54,7 +56,6 @@ import android.content.ContentResolver; import android.content.Context; import android.net.INetd; import android.net.Network; -import android.net.connectivity.ConnectivityCompatChanges; import android.net.mdns.aidl.DiscoveryInfo; import android.net.mdns.aidl.GetAddressInfo; import android.net.mdns.aidl.IMDnsEventListener; @@ -190,7 +191,9 @@ public class NsdServiceTest { } @Test - @DisableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) + @DisableCompatChanges({ + RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER, + ENABLE_PLATFORM_MDNS_BACKEND}) public void testPreSClients() throws Exception { // Pre S client connected, the daemon should be started. connectClient(mService); @@ -217,7 +220,8 @@ public class NsdServiceTest { } @Test - @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) + @EnableCompatChanges(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testNoDaemonStartedWhenClientsConnect() throws Exception { // Creating an NsdManager will not cause daemon startup. connectClient(mService); @@ -251,7 +255,8 @@ public class NsdServiceTest { } @Test - @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) + @EnableCompatChanges(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testClientRequestsAreGCedAtDisconnection() throws Exception { final NsdManager client = connectClient(mService); final INsdManagerCallback cb1 = getCallback(); @@ -294,7 +299,8 @@ public class NsdServiceTest { } @Test - @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) + @EnableCompatChanges(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testCleanupDelayNoRequestActive() throws Exception { final NsdManager client = connectClient(mService); @@ -330,6 +336,7 @@ public class NsdServiceTest { } @Test + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testDiscoverOnTetheringDownstream() throws Exception { final NsdManager client = connectClient(mService); final int interfaceIdx = 123; @@ -420,6 +427,7 @@ public class NsdServiceTest { } @Test + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testDiscoverOnBlackholeNetwork() throws Exception { final NsdManager client = connectClient(mService); final DiscoveryListener discListener = mock(DiscoveryListener.class); @@ -449,6 +457,7 @@ public class NsdServiceTest { } @Test + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testServiceRegistrationSuccessfulAndFailed() throws Exception { final NsdManager client = connectClient(mService); final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); @@ -495,6 +504,7 @@ public class NsdServiceTest { } @Test + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testServiceDiscoveryFailed() throws Exception { final NsdManager client = connectClient(mService); final DiscoveryListener discListener = mock(DiscoveryListener.class); @@ -521,6 +531,7 @@ public class NsdServiceTest { } @Test + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testServiceResolutionFailed() throws Exception { final NsdManager client = connectClient(mService); final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); @@ -551,6 +562,7 @@ public class NsdServiceTest { } @Test + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testGettingAddressFailed() throws Exception { final NsdManager client = connectClient(mService); final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); @@ -597,6 +609,7 @@ public class NsdServiceTest { } @Test + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testNoCrashWhenProcessResolutionAfterBinderDied() throws Exception { final NsdManager client = connectClient(mService); final INsdManagerCallback cb = getCallback(); @@ -616,6 +629,7 @@ public class NsdServiceTest { } @Test + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testStopServiceResolution() { final NsdManager client = connectClient(mService); final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); @@ -638,6 +652,7 @@ public class NsdServiceTest { } @Test + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testStopResolutionFailed() { final NsdManager client = connectClient(mService); final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); @@ -662,6 +677,7 @@ public class NsdServiceTest { } @Test @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testStopResolutionDuringGettingAddress() throws RemoteException { final NsdManager client = connectClient(mService); final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); @@ -823,6 +839,7 @@ public class NsdServiceTest { } @Test + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testMdnsDiscoveryManagerFeature() { // Create NsdService w/o feature enabled. final NsdManager client = connectClient(mService); @@ -1012,6 +1029,7 @@ public class NsdServiceTest { } @Test + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testMdnsAdvertiserFeatureFlagging() { // Create NsdService w/o feature enabled. final NsdManager client = connectClient(mService); @@ -1047,6 +1065,7 @@ public class NsdServiceTest { } @Test + @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) public void testTypeSpecificFeatureFlagging() { doReturn("_type1._tcp:flag1,_type2._tcp:flag2").when(mDeps).getTypeAllowlistFlags(); doReturn(true).when(mDeps).isFeatureEnabled(any(), @@ -1234,6 +1253,37 @@ public class NsdServiceTest { assertEquals("_TEST._sub._999._tcp", constructServiceType(serviceType4)); } + @Test + @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) + public void testEnablePlatformMdnsBackend() { + final NsdManager client = connectClient(mService); + final NsdServiceInfo regInfo = new NsdServiceInfo("a".repeat(70), SERVICE_TYPE); + final Network network = new Network(999); + regInfo.setHostAddresses(List.of(parseNumericAddress("192.0.2.123"))); + regInfo.setPort(12345); + regInfo.setAttribute("testattr", "testvalue"); + regInfo.setNetwork(network); + + // Verify the registration uses MdnsAdvertiser + final RegistrationListener regListener = mock(RegistrationListener.class); + client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener); + waitForIdle(); + verify(mSocketProvider).startMonitoringSockets(); + verify(mAdvertiser).addService(anyInt(), any()); + + // Verify the discovery uses MdnsDiscoveryManager + final DiscoveryListener discListener = mock(DiscoveryListener.class); + client.discoverServices(SERVICE_TYPE, PROTOCOL, network, r -> r.run(), discListener); + waitForIdle(); + verify(mDiscoveryManager).registerListener(anyString(), any(), any()); + + // Verify the discovery uses MdnsDiscoveryManager + final ResolveListener resolveListener = mock(ResolveListener.class); + client.resolveService(regInfo, r -> r.run(), resolveListener); + waitForIdle(); + verify(mDiscoveryManager, times(2)).registerListener(anyString(), any(), any()); + } + private void waitForIdle() { HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS); } @@ -1241,7 +1291,8 @@ public class NsdServiceTest { NsdService makeService() { final NsdService service = new NsdService(mContext, mHandler, CLEANUP_DELAY_MS, mDeps) { @Override - public INsdServiceConnector connect(INsdManagerCallback baseCb) { + public INsdServiceConnector connect(INsdManagerCallback baseCb, + boolean runNewMdnsBackend) { // Wrap the callback in a transparent mock, to mock asBinder returning a // LinkToDeathRecorder. This will allow recording the binder death recipient // registered on the callback. Use a transparent mock and not a spy as the actual @@ -1250,7 +1301,7 @@ public class NsdServiceTest { AdditionalAnswers.delegatesTo(baseCb)); doReturn(new LinkToDeathRecorder()).when(cb).asBinder(); mCreatedCallbacks.add(cb); - return super.connect(cb); + return super.connect(cb, runNewMdnsBackend); } }; return service;