Search code examples
javaandroidservice-discoverynsd

Android NSD: Why service type don't match


I've created an app to test the network service discovery (NSD) on android.

The failing scenario

Two devices running the app. I hit register on the first device and after I see a toast that confirms the registration I hit discover on the other.

The service is discovered by the second device but it's type its unknown. From debuging I saw that the unknown service type is

_mynsd._tcp.

but when the service is registered the type is set to

_mynsd._tcp // (without the dot at the end)

I don't believe I have done something that changes the service type. Here is the Activity:

public class MainActivity extends AppCompatActivity {
    private static final String SERVICE_TYPE = "_mynsd._tcp";

    private ServerSocket serverSocket;
    private int localPort;

    private NsdManager.RegistrationListener registrationListener;
    private NsdManager.DiscoveryListener discoveryListener;
    private NsdManager nsdManager;
    private String serviceName;
    private NsdServiceInfo service;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initializeRegistrationListener();
        initializeServerSocket();
        nsdManager = (NsdManager) getSystemService(NSD_SERVICE);
    }

    public void onRegisterClicked(View view) {
        registerService(localPort);
    }

    public void onDiscoverClicked(View view) {
        if (discoveryListener == null)
            initializeDiscoveryListener();
        nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener);
    }

    public void onConnectClicked(View view) {

    }

    @Override
    protected void onPause() {
        super.onPause();
        if (nsdManager != null)
            nsdManager.unregisterService(registrationListener);
    }

    public void registerService(int port) {
        NsdServiceInfo serviceInfo = new NsdServiceInfo();
        serviceInfo.setServiceName("MyNsd");
        serviceInfo.setServiceType(SERVICE_TYPE);
        serviceInfo.setPort(port);

        nsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener);
    }

    public void initializeServerSocket() {
        try {
            serverSocket = new ServerSocket(0);
            localPort = serverSocket.getLocalPort();
        } catch (IOException e) {
            Toast.makeText(this, "Error with server socket", Toast.LENGTH_SHORT).show();
        }
    }

    public void initializeDiscoveryListener() {
        discoveryListener = new NsdManager.DiscoveryListener() {
            @Override
            public void onStartDiscoveryFailed(String s, int i) {
                Toast.makeText(MainActivity.this, "Start discovery failed", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onStopDiscoveryFailed(String s, int i) {
                Toast.makeText(MainActivity.this, "Stop discovery failed ", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onDiscoveryStarted(String s) {
                Toast.makeText(MainActivity.this, "Started discovery", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onDiscoveryStopped(String s) {
                Toast.makeText(MainActivity.this, "Stopped discovery", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onServiceFound(NsdServiceInfo nsdServiceInfo) {
                Toast.makeText(MainActivity.this, nsdServiceInfo.getServiceName() + " service found", Toast.LENGTH_SHORT).show();

                if (!nsdServiceInfo.getServiceType().equals(SERVICE_TYPE)) {
                    Toast.makeText(MainActivity.this, "Unknown service type "+nsdServiceInfo.getServiceType(), Toast.LENGTH_SHORT).show();
                } else if (nsdServiceInfo.getServiceName().equals(serviceName)) {
                    // The one who registered the service is the one which actually holds the
                    // service name and its not null, so if we are here its the same machine
                    // who registered the service in the first place.
                    Toast.makeText(MainActivity.this, "Same machine: " + serviceName, Toast.LENGTH_SHORT).show();
                } else if (nsdServiceInfo.getServiceName().contains("MyNsd")) {
                    nsdManager.resolveService(nsdServiceInfo, new NsdManager.ResolveListener() {
                        @Override
                        public void onResolveFailed(NsdServiceInfo nsdServiceInfo, int i) {
                            Toast.makeText(MainActivity.this, "Could not resolve " + nsdServiceInfo.getServiceName(), Toast.LENGTH_SHORT).show();
                        }

                        @Override
                        public void onServiceResolved(NsdServiceInfo nsdServiceInfo) {
                            Toast.makeText(MainActivity.this, "Resolved  " + nsdServiceInfo.getServiceName(), Toast.LENGTH_SHORT).show();
                            if (nsdServiceInfo.getServiceName().equals(serviceName)){
                                Toast.makeText(MainActivity.this, "Same IP", Toast.LENGTH_SHORT).show();
                                return;
                            }

                            service = nsdServiceInfo;
                            int port = service.getPort();
                            InetAddress host = service.getHost();
                        }
                    });
                }
            }

            @Override
            public void onServiceLost(NsdServiceInfo nsdServiceInfo) {
                Toast.makeText(MainActivity.this, nsdServiceInfo.getServiceName() + " service lost", Toast.LENGTH_SHORT).show();
            }
        };
    }

    public void initializeRegistrationListener() {
        registrationListener = new NsdManager.RegistrationListener() {
            @Override
            public void onRegistrationFailed(NsdServiceInfo nsdServiceInfo, int i) {
                Toast.makeText(MainActivity.this, "Registration failed!", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onUnregistrationFailed(NsdServiceInfo nsdServiceInfo, int i) {
                Toast.makeText(MainActivity.this, "Unregistration failed", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) {
                Toast.makeText(MainActivity.this, "Service registered", Toast.LENGTH_SHORT).show();
                serviceName = nsdServiceInfo.getServiceName();
            }

            @Override
            public void onServiceUnregistered(NsdServiceInfo nsdServiceInfo) {
                Toast.makeText(MainActivity.this, "Service unregistered", Toast.LENGTH_SHORT).show();
            }
        };
    }
}

and here is the layout:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/register"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:onClick="onRegisterClicked"
        android:text="Register"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/discover"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:onClick="onDiscoverClicked"
        android:text="Discover"
        app:layout_constraintStart_toEndOf="@+id/register"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/connect"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:onClick="onConnectClicked"
        android:text="connect"
        app:layout_constraintStart_toEndOf="@+id/discover"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

Solution

  • you have to register it in DNS in form of a SRV record, only then it would become known. possibly, if you would set one device as the name-server for the other device, this registration on the localhost might work out. explained it once here; that's Cloud DNS, but any local name-server would suffice for localdomain.

    to answer the actual question: it's absolute domain name (with a trailing .) vs. relative domain name.