My android app discovers servers on the network using the android.net.nsd package. This works well for about 98% of my users. For the remaining 2%, however, DiscoveryListener.onServiceFound() ist not called reliably. I cannot see any similarities between the devices concerned. The problem occures with:
- various android versions (e.g. 9, 12, ...)
- various smartphones from various manufacturers (e.g. Google Pixel 3a, Gigaset GS110, ...)
- various network routers (e.g. Fritz!Box, TPLink with open WRT software, ...)
- networks with and without internet connection Other smartphones that are connected to the same network at the same time can find the server without any problems. After manually entering the ip-address and port, the affected devices also connect to the server.
I have created a test app in which the program code is reduced to a minimum. onServiceFound() still not called reliably on the affected devices. Accquiring a multicast look does not bring any improvement.I want all smartphones to find the server reliably, as this is a key function of the app.
I have added the following permissions to the AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
MainActivity.java:
public class MainActivity extends AppCompatActivity {
@NonNull
private static final String TAG = "MainActivity";
@NonNull
private static final String SERVICE_TYPE = "_esu-mrtp._tcp.";
@Nullable
private NsdManager.DiscoveryListener discoveryListener;
@Nullable
private NsdManager nsdManager;
@Nullable
private WifiManager.MulticastLock multicastLock;
@Nullable
private View viewStarted = null;
@Nullable
private View viewFound = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.viewStarted = findViewById(R.id.view_started);
this.viewFound = findViewById(R.id.view_found);
if (null == this.viewStarted || null == this.viewFound) {
Toast.makeText(this, "Fehler beim Start.", Toast.LENGTH_LONG).show();
}
}
@Override
protected void onStart() {
super.onStart();
if (null == this.multicastLock) {
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
if (null != wifiManager) {
this.multicastLock = wifiManager.createMulticastLock("multicastLock");
// tag "multicastLock" is used for the MulticastLock to identify it in debugging messages.
this.multicastLock.setReferenceCounted(true);
// presumably refCounted = false would make no difference here.
} else Toast.makeText(this, "Kein WiFi-Manager", Toast.LENGTH_LONG).show();;
}
if (null != this.multicastLock) this.multicastLock.acquire();
initializeDiscoveryListener();
getNsdManager().discoverServices(
SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, this.discoveryListener);
}
@Override
protected void onStop() {
super.onStop();
stopServiceDiscovery();
if (null != this.multicastLock) this.multicastLock.release();
}
@NonNull
private NsdManager getNsdManager() {
if (null == this.nsdManager) {
this.nsdManager = (NsdManager) getApplicationContext().getSystemService(Context.NSD_SERVICE);
if (null == this.nsdManager) Toast.makeText(this, "Kein NSD-Manager!", Toast.LENGTH_LONG).show();
}
return this.nsdManager;
}
private void initializeDiscoveryListener() {
if (null == this.discoveryListener) {
this.discoveryListener = new NsdManager.DiscoveryListener() {
// Called as soon as service discovery begins.
@Override
public void onDiscoveryStarted(String regType) {
runOnUiThread(() -> {
Log.i(TAG, "Service discovery started");
if (null != viewStarted) viewStarted.setVisibility(View.VISIBLE);
});
}
@Override
public void onServiceFound(NsdServiceInfo service) {
runOnUiThread(() -> {
// A service was found! Do something with it.
Log.i(TAG, "Service discovery success" + service);
if (service.getServiceType().equals(SERVICE_TYPE)) {
if (null != viewFound) viewFound.setVisibility(View.VISIBLE);
}
});
}
@Override
public void onServiceLost(NsdServiceInfo service) {
runOnUiThread(() -> {
// When the network service is no longer available.
// Internal bookkeeping code goes here.
Log.i(TAG, "service lost: " + service);
});
}
@Override
public void onDiscoveryStopped(String serviceType) {
runOnUiThread(() -> {
Log.i(TAG, "Discovery stopped: " + serviceType);
});
}
@Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
runOnUiThread(() -> {
Log.i(TAG, "Discovery failed: Error code:" + errorCode);
stopServiceDiscovery();
});
}
@Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
runOnUiThread(() -> {
Log.i(TAG, "Discovery failed: Error code:" + errorCode);
stopServiceDiscovery();
});
}
};
}
}
private void stopServiceDiscovery() {
if (null == this.discoveryListener) return;
getNsdManager().stopServiceDiscovery(this.discoveryListener);
this.discoveryListener = null;
if (null != this.viewStarted) this.viewStarted.setVisibility(View.INVISIBLE);
if (null != this.viewFound) this.viewFound.setVisibility(View.INVISIBLE);
}
}