Loading services/core/java/com/android/server/NsdService.java +81 −106 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ import android.util.SparseArray; import java.io.FileDescriptor; import java.io.PrintWriter; import java.net.InetAddress; import java.util.Arrays; import java.util.HashMap; import java.util.concurrent.CountDownLatch; Loading @@ -65,8 +66,8 @@ public class NsdService extends INsdManager.Stub { private final Context mContext; private final NsdSettings mNsdSettings; private final NsdStateMachine mNsdStateMachine; private final NativeDaemonConnector mNativeConnector; private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1); private final DaemonConnection mDaemon; private final NativeCallbackReceiver mDaemonCallback; /** * Clients receiving asynchronous messages Loading Loading @@ -546,18 +547,13 @@ public class NsdService extends INsdManager.Stub { } @VisibleForTesting NsdService(Context ctx, NsdSettings settings, Handler handler) { NsdService(Context ctx, NsdSettings settings, Handler handler, DaemonConnectionSupplier fn) { mContext = ctx; mNsdSettings = settings; NativeCallbackReceiver callback = new NativeCallbackReceiver(); mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null); mNsdStateMachine = new NsdStateMachine(TAG, handler); mNsdStateMachine.start(); Thread th = new Thread(mNativeConnector, MDNS_TAG); th.start(); mDaemonCallback = new NativeCallbackReceiver(); mDaemon = fn.get(mDaemonCallback); } public static NsdService create(Context context) throws InterruptedException { Loading @@ -565,8 +561,8 @@ public class NsdService extends INsdManager.Stub { HandlerThread thread = new HandlerThread(TAG); thread.start(); Handler handler = new Handler(thread.getLooper()); NsdService service = new NsdService(context, settings, handler); service.mNativeDaemonConnected.await(); NsdService service = new NsdService(context, settings, handler, DaemonConnection::new); service.mDaemonCallback.awaitConnection(); return service; } Loading Loading @@ -665,14 +661,23 @@ public class NsdService extends INsdManager.Stub { } class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks { private final CountDownLatch connected = new CountDownLatch(1); public void awaitConnection() throws InterruptedException { connected.await(); } @Override public void onDaemonConnected() { mNativeDaemonConnected.countDown(); connected.countDown(); } @Override public boolean onCheckHoldWakeLock(int code) { return false; } @Override public boolean onEvent(int code, String raw, String[] cooked) { // TODO: NDC translates a message to a callback, we could enhance NDC to // directly interact with a state machine through messages Loading @@ -682,132 +687,102 @@ public class NsdService extends INsdManager.Stub { } } private boolean startMDnsDaemon() { if (DBG) Slog.d(TAG, "startMDnsDaemon"); try { mNativeConnector.execute("mdnssd", "start-service"); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to start daemon" + e); return false; interface DaemonConnectionSupplier { DaemonConnection get(NativeCallbackReceiver callback); } return true; @VisibleForTesting public static class DaemonConnection { final NativeDaemonConnector mNativeConnector; DaemonConnection(NativeCallbackReceiver callback) { mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null); new Thread(mNativeConnector, MDNS_TAG).start(); } private boolean stopMDnsDaemon() { if (DBG) Slog.d(TAG, "stopMDnsDaemon"); public boolean execute(Object... args) { if (DBG) { Slog.d(TAG, "mdnssd " + Arrays.toString(args)); } try { mNativeConnector.execute("mdnssd", "stop-service"); mNativeConnector.execute("mdnssd", args); } catch (NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to start daemon" + e); Slog.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e); return false; } return true; } private boolean registerService(int regId, NsdServiceInfo service) { if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service); public boolean execute(Command cmd) { if (DBG) { Slog.d(TAG, cmd.toString()); } try { Command cmd = new Command("mdnssd", "register", regId, service.getServiceName(), service.getServiceType(), service.getPort(), Base64.encodeToString(service.getTxtRecord(), Base64.DEFAULT) .replace("\n", "")); mNativeConnector.execute(cmd); } catch (NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to execute registerService " + e); Slog.e(TAG, "Failed to execute " + cmd, e); return false; } return true; } } private boolean unregisterService(int regId) { if (DBG) Slog.d(TAG, "unregisterService: " + regId); try { mNativeConnector.execute("mdnssd", "stop-register", regId); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to execute unregisterService " + e); return false; private boolean startMDnsDaemon() { return mDaemon.execute("start-service"); } return true; private boolean stopMDnsDaemon() { return mDaemon.execute("stop-service"); } private boolean registerService(int regId, NsdServiceInfo service) { if (DBG) { Slog.d(TAG, "registerService: " + regId + " " + service); } String name = service.getServiceName(); String type = service.getServiceType(); int port = service.getPort(); byte[] textRecord = service.getTxtRecord(); String record = Base64.encodeToString(textRecord, Base64.DEFAULT).replace("\n", ""); Command cmd = new Command("mdnssd", "register", regId, name, type, port, record); return mDaemon.execute(cmd); } private boolean unregisterService(int regId) { return mDaemon.execute("stop-register", regId); } private boolean updateService(int regId, DnsSdTxtRecord t) { if (DBG) Slog.d(TAG, "updateService: " + regId + " " + t); try { if (t == null) return false; mNativeConnector.execute("mdnssd", "update", regId, t.size(), t.getRawData()); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to updateServices " + e); if (t == null) { return false; } return true; return mDaemon.execute("update", regId, t.size(), t.getRawData()); } private boolean discoverServices(int discoveryId, String serviceType) { if (DBG) Slog.d(TAG, "discoverServices: " + discoveryId + " " + serviceType); try { mNativeConnector.execute("mdnssd", "discover", discoveryId, serviceType); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to discoverServices " + e); return false; } return true; return mDaemon.execute("discover", discoveryId, serviceType); } private boolean stopServiceDiscovery(int discoveryId) { if (DBG) Slog.d(TAG, "stopServiceDiscovery: " + discoveryId); try { mNativeConnector.execute("mdnssd", "stop-discover", discoveryId); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to stopServiceDiscovery " + e); return false; } return true; return mDaemon.execute("stop-discover", discoveryId); } private boolean resolveService(int resolveId, NsdServiceInfo service) { if (DBG) Slog.d(TAG, "resolveService: " + resolveId + " " + service); try { mNativeConnector.execute("mdnssd", "resolve", resolveId, service.getServiceName(), service.getServiceType(), "local."); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to resolveService " + e); return false; } return true; String name = service.getServiceName(); String type = service.getServiceType(); return mDaemon.execute("resolve", resolveId, name, type, "local."); } private boolean stopResolveService(int resolveId) { if (DBG) Slog.d(TAG, "stopResolveService: " + resolveId); try { mNativeConnector.execute("mdnssd", "stop-resolve", resolveId); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to stop resolve " + e); return false; } return true; return mDaemon.execute("stop-resolve", resolveId); } private boolean getAddrInfo(int resolveId, String hostname) { if (DBG) Slog.d(TAG, "getAdddrInfo: " + resolveId); try { mNativeConnector.execute("mdnssd", "getaddrinfo", resolveId, hostname); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to getAddrInfo " + e); return false; } return true; return mDaemon.execute("getaddrinfo", resolveId, hostname); } private boolean stopGetAddrInfo(int resolveId) { if (DBG) Slog.d(TAG, "stopGetAdddrInfo: " + resolveId); try { mNativeConnector.execute("mdnssd", "stop-getaddrinfo", resolveId); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to stopGetAddrInfo " + e); return false; } return true; return mDaemon.execute("stop-getaddrinfo", resolveId); } @Override Loading tests/net/java/android/net/nsd/NsdServiceTest.java +22 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,11 @@ package com.android.server; import static org.mockito.Mockito.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.os.Handler; Loading @@ -25,6 +30,9 @@ import android.os.test.TestLooper; import android.content.Context; import android.content.ContentResolver; import android.net.nsd.NsdManager; import com.android.server.NsdService.DaemonConnection; import com.android.server.NsdService.DaemonConnectionSupplier; import com.android.server.NsdService.NativeCallbackReceiver; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import org.junit.Before; Loading @@ -34,7 +42,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; // TODOs: // - test daemon connection // - test client disconnects // - test client can send requests and receive replies // - test NSD_ON ENABLE/DISABLED listening Loading @@ -45,6 +52,8 @@ public class NsdServiceTest { @Mock Context mContext; @Mock ContentResolver mResolver; @Mock NsdService.NsdSettings mSettings; @Mock DaemonConnection mDaemon; NativeCallbackReceiver mDaemonCallback; TestLooper mLooper; TestHandler mHandler; Loading @@ -58,8 +67,13 @@ public class NsdServiceTest { @Test public void testClientsCanConnect() { when(mSettings.isEnabled()).thenReturn(true); NsdService service = makeService(); NsdManager client1 = connectClient(service); verify(mDaemon, timeout(100).times(1)).execute("start-service"); NsdManager client2 = connectClient(service); // TODO: disconnect client1 Loading @@ -67,7 +81,13 @@ public class NsdServiceTest { } NsdService makeService() { return new NsdService(mContext, mSettings, mHandler); DaemonConnectionSupplier supplier = (callback) -> { mDaemonCallback = callback; return mDaemon; }; NsdService service = new NsdService(mContext, mSettings, mHandler, supplier); verify(mDaemon, never()).execute(any(String.class)); return service; } NsdManager connectClient(NsdService service) { Loading Loading
services/core/java/com/android/server/NsdService.java +81 −106 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ import android.util.SparseArray; import java.io.FileDescriptor; import java.io.PrintWriter; import java.net.InetAddress; import java.util.Arrays; import java.util.HashMap; import java.util.concurrent.CountDownLatch; Loading @@ -65,8 +66,8 @@ public class NsdService extends INsdManager.Stub { private final Context mContext; private final NsdSettings mNsdSettings; private final NsdStateMachine mNsdStateMachine; private final NativeDaemonConnector mNativeConnector; private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1); private final DaemonConnection mDaemon; private final NativeCallbackReceiver mDaemonCallback; /** * Clients receiving asynchronous messages Loading Loading @@ -546,18 +547,13 @@ public class NsdService extends INsdManager.Stub { } @VisibleForTesting NsdService(Context ctx, NsdSettings settings, Handler handler) { NsdService(Context ctx, NsdSettings settings, Handler handler, DaemonConnectionSupplier fn) { mContext = ctx; mNsdSettings = settings; NativeCallbackReceiver callback = new NativeCallbackReceiver(); mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null); mNsdStateMachine = new NsdStateMachine(TAG, handler); mNsdStateMachine.start(); Thread th = new Thread(mNativeConnector, MDNS_TAG); th.start(); mDaemonCallback = new NativeCallbackReceiver(); mDaemon = fn.get(mDaemonCallback); } public static NsdService create(Context context) throws InterruptedException { Loading @@ -565,8 +561,8 @@ public class NsdService extends INsdManager.Stub { HandlerThread thread = new HandlerThread(TAG); thread.start(); Handler handler = new Handler(thread.getLooper()); NsdService service = new NsdService(context, settings, handler); service.mNativeDaemonConnected.await(); NsdService service = new NsdService(context, settings, handler, DaemonConnection::new); service.mDaemonCallback.awaitConnection(); return service; } Loading Loading @@ -665,14 +661,23 @@ public class NsdService extends INsdManager.Stub { } class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks { private final CountDownLatch connected = new CountDownLatch(1); public void awaitConnection() throws InterruptedException { connected.await(); } @Override public void onDaemonConnected() { mNativeDaemonConnected.countDown(); connected.countDown(); } @Override public boolean onCheckHoldWakeLock(int code) { return false; } @Override public boolean onEvent(int code, String raw, String[] cooked) { // TODO: NDC translates a message to a callback, we could enhance NDC to // directly interact with a state machine through messages Loading @@ -682,132 +687,102 @@ public class NsdService extends INsdManager.Stub { } } private boolean startMDnsDaemon() { if (DBG) Slog.d(TAG, "startMDnsDaemon"); try { mNativeConnector.execute("mdnssd", "start-service"); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to start daemon" + e); return false; interface DaemonConnectionSupplier { DaemonConnection get(NativeCallbackReceiver callback); } return true; @VisibleForTesting public static class DaemonConnection { final NativeDaemonConnector mNativeConnector; DaemonConnection(NativeCallbackReceiver callback) { mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null); new Thread(mNativeConnector, MDNS_TAG).start(); } private boolean stopMDnsDaemon() { if (DBG) Slog.d(TAG, "stopMDnsDaemon"); public boolean execute(Object... args) { if (DBG) { Slog.d(TAG, "mdnssd " + Arrays.toString(args)); } try { mNativeConnector.execute("mdnssd", "stop-service"); mNativeConnector.execute("mdnssd", args); } catch (NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to start daemon" + e); Slog.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e); return false; } return true; } private boolean registerService(int regId, NsdServiceInfo service) { if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service); public boolean execute(Command cmd) { if (DBG) { Slog.d(TAG, cmd.toString()); } try { Command cmd = new Command("mdnssd", "register", regId, service.getServiceName(), service.getServiceType(), service.getPort(), Base64.encodeToString(service.getTxtRecord(), Base64.DEFAULT) .replace("\n", "")); mNativeConnector.execute(cmd); } catch (NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to execute registerService " + e); Slog.e(TAG, "Failed to execute " + cmd, e); return false; } return true; } } private boolean unregisterService(int regId) { if (DBG) Slog.d(TAG, "unregisterService: " + regId); try { mNativeConnector.execute("mdnssd", "stop-register", regId); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to execute unregisterService " + e); return false; private boolean startMDnsDaemon() { return mDaemon.execute("start-service"); } return true; private boolean stopMDnsDaemon() { return mDaemon.execute("stop-service"); } private boolean registerService(int regId, NsdServiceInfo service) { if (DBG) { Slog.d(TAG, "registerService: " + regId + " " + service); } String name = service.getServiceName(); String type = service.getServiceType(); int port = service.getPort(); byte[] textRecord = service.getTxtRecord(); String record = Base64.encodeToString(textRecord, Base64.DEFAULT).replace("\n", ""); Command cmd = new Command("mdnssd", "register", regId, name, type, port, record); return mDaemon.execute(cmd); } private boolean unregisterService(int regId) { return mDaemon.execute("stop-register", regId); } private boolean updateService(int regId, DnsSdTxtRecord t) { if (DBG) Slog.d(TAG, "updateService: " + regId + " " + t); try { if (t == null) return false; mNativeConnector.execute("mdnssd", "update", regId, t.size(), t.getRawData()); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to updateServices " + e); if (t == null) { return false; } return true; return mDaemon.execute("update", regId, t.size(), t.getRawData()); } private boolean discoverServices(int discoveryId, String serviceType) { if (DBG) Slog.d(TAG, "discoverServices: " + discoveryId + " " + serviceType); try { mNativeConnector.execute("mdnssd", "discover", discoveryId, serviceType); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to discoverServices " + e); return false; } return true; return mDaemon.execute("discover", discoveryId, serviceType); } private boolean stopServiceDiscovery(int discoveryId) { if (DBG) Slog.d(TAG, "stopServiceDiscovery: " + discoveryId); try { mNativeConnector.execute("mdnssd", "stop-discover", discoveryId); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to stopServiceDiscovery " + e); return false; } return true; return mDaemon.execute("stop-discover", discoveryId); } private boolean resolveService(int resolveId, NsdServiceInfo service) { if (DBG) Slog.d(TAG, "resolveService: " + resolveId + " " + service); try { mNativeConnector.execute("mdnssd", "resolve", resolveId, service.getServiceName(), service.getServiceType(), "local."); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to resolveService " + e); return false; } return true; String name = service.getServiceName(); String type = service.getServiceType(); return mDaemon.execute("resolve", resolveId, name, type, "local."); } private boolean stopResolveService(int resolveId) { if (DBG) Slog.d(TAG, "stopResolveService: " + resolveId); try { mNativeConnector.execute("mdnssd", "stop-resolve", resolveId); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to stop resolve " + e); return false; } return true; return mDaemon.execute("stop-resolve", resolveId); } private boolean getAddrInfo(int resolveId, String hostname) { if (DBG) Slog.d(TAG, "getAdddrInfo: " + resolveId); try { mNativeConnector.execute("mdnssd", "getaddrinfo", resolveId, hostname); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to getAddrInfo " + e); return false; } return true; return mDaemon.execute("getaddrinfo", resolveId, hostname); } private boolean stopGetAddrInfo(int resolveId) { if (DBG) Slog.d(TAG, "stopGetAdddrInfo: " + resolveId); try { mNativeConnector.execute("mdnssd", "stop-getaddrinfo", resolveId); } catch(NativeDaemonConnectorException e) { Slog.e(TAG, "Failed to stopGetAddrInfo " + e); return false; } return true; return mDaemon.execute("stop-getaddrinfo", resolveId); } @Override Loading
tests/net/java/android/net/nsd/NsdServiceTest.java +22 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,11 @@ package com.android.server; import static org.mockito.Mockito.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.os.Handler; Loading @@ -25,6 +30,9 @@ import android.os.test.TestLooper; import android.content.Context; import android.content.ContentResolver; import android.net.nsd.NsdManager; import com.android.server.NsdService.DaemonConnection; import com.android.server.NsdService.DaemonConnectionSupplier; import com.android.server.NsdService.NativeCallbackReceiver; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import org.junit.Before; Loading @@ -34,7 +42,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; // TODOs: // - test daemon connection // - test client disconnects // - test client can send requests and receive replies // - test NSD_ON ENABLE/DISABLED listening Loading @@ -45,6 +52,8 @@ public class NsdServiceTest { @Mock Context mContext; @Mock ContentResolver mResolver; @Mock NsdService.NsdSettings mSettings; @Mock DaemonConnection mDaemon; NativeCallbackReceiver mDaemonCallback; TestLooper mLooper; TestHandler mHandler; Loading @@ -58,8 +67,13 @@ public class NsdServiceTest { @Test public void testClientsCanConnect() { when(mSettings.isEnabled()).thenReturn(true); NsdService service = makeService(); NsdManager client1 = connectClient(service); verify(mDaemon, timeout(100).times(1)).execute("start-service"); NsdManager client2 = connectClient(service); // TODO: disconnect client1 Loading @@ -67,7 +81,13 @@ public class NsdServiceTest { } NsdService makeService() { return new NsdService(mContext, mSettings, mHandler); DaemonConnectionSupplier supplier = (callback) -> { mDaemonCallback = callback; return mDaemon; }; NsdService service = new NsdService(mContext, mSettings, mHandler, supplier); verify(mDaemon, never()).execute(any(String.class)); return service; } NsdManager connectClient(NsdService service) { Loading