During the first implementation of the NSDManager using the examples and the tutorial on the developer page , the application successfully started the discovery and found the devices.
However now it seems to be broken...
When the program is started, after some initialization, the code enters the following method and successfully run:
public void discoverServices() {
Log.d(TAG, "Initializing discovery on NSD");
mNsdManager.discoverServices(
SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
}
The log-message is received. After a good while (say approx 5 mins.) this is outputted from the program:
05-21 11:08:32.518: E/NsdCamera(12236): Discovery failed: Error code:0
05-21 11:08:32.518: W/dalvikvm(12236): threadid=12: thread exiting with uncaught exception (group=0x40c9c930)
05-21 11:08:32.518: E/AndroidRuntime(12236): FATAL EXCEPTION: NsdManager
05-21 11:08:32.518: E/AndroidRuntime(12236): java.lang.NullPointerException
05-21 11:08:32.518: E/AndroidRuntime(12236): at android.net.nsd.NsdManager$ServiceHandler.handleMessage(NsdManager.java:338)
05-21 11:08:32.518: E/AndroidRuntime(12236): at android.os.Handler.dispatchMessage(Handler.java:99)
05-21 11:08:32.518: E/AndroidRuntime(12236): at android.os.Looper.loop(Looper.java:137)
05-21 11:08:32.518: E/AndroidRuntime(12236): at android.os.HandlerThread.run(HandlerThread.java:60)
Also from the services:
05-21 11:50:49.108: E/NativeDaemonConnector.ResponseQueue(8858): Timeout waiting for response
05-21 11:50:49.108: E/mDnsConnector(8858): timed-out waiting for response to 10 mdnssd discover 6 _http._tcp.
05-21 11:50:49.108: E/NsdService(8858): Failed to discoverServices com.android.server.NativeDaemonConnector$NativeDaemonFailureException: command '10 mdnssd discover 6 _http._tcp.' failed with 'null'
Error code "0" is described in the NSDManager class as an internal error.
The major updates I did was the access to the context in the helper-class called NsdCamera.
Here is some probably evil code-snippets:
Helper-class constructor:
public NsdCamera(CameraChooseActivity context) {
mContext = context;
updateUI = new UpdateUI();
mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
mServiceName = new Vector<NsdServiceInfo>();
Helper-class NSD initialization:
public void initializeNsd() {
initializeDiscoveryListener();
}
public void initializeDiscoveryListener() {
mDiscoveryListener = new NsdManager.DiscoveryListener() {
#Override
public void onDiscoveryStarted(String regType) {
Log.d(TAG, "Service discovery started");
}
/**
* A name check to see if the DNS discovery was correct. Checks if it contains
* AXIS and has the desired MAC address-space
* #param hostname ,the inputted hostname from the discovery cycle
* #return true if it's an Axis camera.
*/
public boolean nameCheck(String hostname){
return (hostname.contains("AXIS") && hostname.contains("00408C"));
}
#Override
public void onServiceFound(NsdServiceInfo service) {
Log.d(TAG, "Service discovery success: " + service.getServiceName());
if (!service.getServiceType().equals(SERVICE_TYPE)) {
Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
} else if (nameCheck(service.getServiceName())){
mServiceName.add(service);
// updateUI.execute(new BundleUI(mContext,service, null));
}
}
#Override
public void onServiceLost(NsdServiceInfo service) {
Log.e(TAG, "service lost" + service);
if(mServiceName.remove(service)){
//TODO
Log.e(TAG, "remove the view, service is lost");
}
}
#Override
public void onDiscoveryStopped(String serviceType) {
Log.i(TAG, "Discovery stopped: " + serviceType);
//Necessary??
mServiceName.clear();
}
#Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
mNsdManager.stopServiceDiscovery(this);
}
#Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
mNsdManager.stopServiceDiscovery(this);
}
};
}
CameraChooseActivity -> onCreate is calling the helper-class
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camerachoose);
//Setup the animation for the text in the Relativelayout
mDescription = (TextSwitcher) findViewById(R.id.camera_add);
mDescription.setFactory(this);
mDescription.setInAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_in));
mDescription.setOutAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_out));
mDescription.setText(getText(R.string.camera_add));
//Building alert dialog
mBuilder = new AlertDialog.Builder(this,AlertDialog.THEME_HOLO_DARK);
mBuilder.setMessage(R.string.dialog_about).setTitle(R.string.action_about);
mBuilder.setIcon(android.R.drawable.ic_dialog_info);
mLayout = (RelativeLayout) findViewById(R.id.layout_camerachoose);
//Initialize the NSD
mNSDHelper = new NsdCamera(this);
mNSDHelper.initializeNsd();
Base on my experience, I suppose this is a listener lifetime issue.
Because you supply two listeners to the system NSD service, one is for startServiceDiscovery() and another for stopServiceDiscovery(). you need to make sure these listeners still alive when the system accesses these listeners.
One fact is that onStartDiscoveryFailed() is called 2 minutes after startServiceDiscovery() is called, it should be a long time compared to the lifetime of the listener.
So if the listener is a local object and is released after calling the startServiceDiscovery(), it maybe cause the NSD service to crash.
public void stopServiceDiscovery (NsdManager.DiscoveryListener
listener)
Stop service discovery initiated with discoverServices(). An active
service discovery is notified to the application with
onDiscoveryStarted(String) and it stays active until the application
invokes a stop service discovery. A successful stop is notified to
with a call to onDiscoveryStopped(String).
Upon failure to stop service discovery, application is notified
through onStopDiscoveryFailed(String, int).
Parameters listener This should be the listener object that was passed
to discoverServices(String, int, NsdManager.DiscoveryListener). It
identifies the discovery that should be stopped and notifies of a
successful stop.
and below snippet make sure do not call any NsdManager api.
#Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
Log.i(TAG, "onStartDiscoveryFailed : Error code:" + errorCode);
}
#Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
Log.i(TAG, "onStopDiscoveryFailed : Error code:" + errorCode);
}
Good Luck.
A simple restart of the DUT proved to be the solution. Must say that the error is pretty odd.
I think the daemon crashed and didn't restart.
(If someone can post an analysis or have a much better solution, please post it)
Related
I have started developing live streaming using kurento media server. I have Installed kurento media server in Ubuntu 14.04TLS. It gets successfully Installed and also started successfully.
I have used STUN server, I have uncommented these two lines
stunServerAddress=<stun_ip_address>
stunServerPort=<stun_port>
And used IP, PORT as follows: 173.194.66.127:19302
.After doing all these thing I started kurento media server and it gets started.
Now I create Android native application by implementing RoomListener Interface.
Here is the Code to connect with Kurento Media Server.
public class MainActivity extends AppCompatActivity implements RoomListener {
private LooperExecutor executor;
private static KurentoRoomAPI kurentoRoomAPI;
private String TAG = "MainActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
executor = new LooperExecutor();
executor.requestStart();
String wsRoomUri = "wss://173.194.66.127:19302/room";
kurentoRoomAPI = new KurentoRoomAPI(executor, wsRoomUri, this);
kurentoRoomAPI.connectWebSocket();
}
#Override
public void onRoomResponse(RoomResponse response) {
Log.d(TAG, "Response");
}
#Override
public void onRoomError(RoomError error) {
Log.d(TAG, "Error: " + error.toString());
}
#Override
public void onRoomNotification(RoomNotification notification) {
Log.d(TAG, "Notification Received");
}
#Override
public void onRoomConnected() {
Log.d(TAG, "Connected");
}
#Override
public void onRoomDisconnected() {
Log.d(TAG, "Room Disconnected");
}
}
But when I run the application, it shows the following error:
Process: com.base.videostreamingkurento, PID: 1880
java.lang.AssertionError: java.net.SocketTimeoutException: failed to connect to /173.194.66.127 (port 19302) after 90000ms
at libcore.io.IoBridge.connect(IoBridge.java:117)
at java.nio.SocketChannelImpl.connect(SocketChannelImpl.java:199)
at org.java_websocket.client.WebSocketClient.interruptableRun(WebSocketClient.java:210)
at org.java_websocket.client.WebSocketClient.run(WebSocketClient.java:188)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.net.SocketTimeoutException: failed to connect to /173.194.66.127 (port 19302) after 90000ms
at libcore.io.IoBridge.connectErrno(IoBridge.java:189)
at libcore.io.IoBridge.connect(IoBridge.java:127)
at libcore.io.IoBridge.connect(IoBridge.java:115)
It seems that you have a little confusion with your deployment and IPs. You are trying to connect, from your client, to the STUN server. Instead, try setting the IP and PORT where your app server is located in this line
String wsRoomUri = "wss://APP_SERVER_IP:8443/room";
If you can draw that down, you'll see more clearly where everything goes and what should connect to what.
I am trying to work with NSD and I find myself stuck at the very begining..
I set up a very basic layout with a single big button. This button's purpose is to start and register a service on my device so other devices would be able to connect to it through LAN. One press of the said button calls the folowing method in my only Activity :
public void startService(View view){
initSocket();
initRegList();
regService();
}
Following the DevBytes: Network Service Discovery video, I implemented the methods called above, like so (pardon my YOLO-ing for debugging purposes) :
public void initSocket(){
try {
mSocket = new ServerSocket(0);
} catch (IOException e) {
e.printStackTrace();
}
mPort = mSocket.getLocalPort();
Log.e("YOLO-PORT", String.valueOf(mPort));
}
public void regService(){
NsdServiceInfo serviceInfo = new NsdServiceInfo();
serviceInfo.setServiceName("MyCoolService");
serviceInfo.setServiceType("_myapp.tcp.");
serviceInfo.setPort(mPort);
mNsdman = (NsdManager) this.getSystemService(this.NSD_SERVICE);
mNsdman.registerService(serviceInfo,NsdManager.PROTOCOL_DNS_SD,mReglist);
}
public void initRegList() {
mReglist = new NsdManager.RegistrationListener() {
#Override
public void onRegistrationFailed(NsdServiceInfo nsdServiceInfo, int i) {
Log.e("YOLO-FAIL", "REG_FAIL, errcode = " + String.valueOf(i));
}
#Override
public void onUnregistrationFailed(NsdServiceInfo nsdServiceInfo, int i) {
Log.e("YOLO-FAIL", "UNREG_FAIL, errcode = " + String.valueOf(i));
}
#Override
public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) {
mServName = nsdServiceInfo.getServiceName();
Log.e("YOLO-NAME", mServName);
}
#Override
public void onServiceUnregistered(NsdServiceInfo nsdServiceInfo) {
Log.e("YOLO-OK", "UNREG");
}
};
}
My problem is I keep falling into onUnregistrationFailed method of the listener with a return code = 0.
Below you will find Logcat entries apearing when I press the said button :
09-14 21:54:03.904 18672-18672/fr.lpnsk.lollibox E/YOLO-PORT﹕ 48321
09-14 21:54:04.124 180-531/? E/MDnsDS﹕ service register request 22 got an error from DNSServiceRegister -65540
09-14 21:54:04.125 538-607/? E/NsdService﹕ Failed to execute registerService com.android.server.NativeDaemonConnector$NativeDaemonArgumentException: command '76 mdnssd register 22 MyCoolService _myapp.tcp. 48321' failed with '501 76 serviceRegister request got an error from DNSServiceRegister'
09-14 21:54:04.126 180-531/? E/MDnsDS﹕ register stop used unknown requestId 22
09-14 21:54:04.126 538-607/? E/NsdService﹕ Failed to execute unregisterService com.android.server.NativeDaemonConnector$NativeDaemonArgumentException: command '77 mdnssd stop-register 22' failed with '501 77 Unknown requestId'
09-14 21:54:04.127 18672-19953/fr.lpnsk.lollibox E/YOLO-FAIL﹕ REG_FAIL, errcode = 0
Am I missing something obvious here ?
Thank you for your help !
Am I missing something obvious here?
Yes. You missed the underscore _ sign before tcp when setting the service type. It should be:
serviceInfo.setServiceType("_myapp._tcp.");
From the official documentation:
...the service type specifies which protocol and transport layer the application uses. The syntax is "_protocol._transportlayer".
Wifi P2P service discovery is not behaving as expected. I am seeing intermittent issues where the DNSSD listeners are not called always and hence I have no clue of nearby devices running the same app. I am using the following two APIs - one to register a service to be discovered by other devices and the other to discover the nearby services running on other devices. Any idea if I am doing anything wrong here or is there some specific sequence of other android API calls that need to be made before I call these APIs to ensure that the listeners are always called whenever there is a new service registered or even if a service is registered before we call the API to discover the local services.
API to register a local service:
private void registerService() {
Map<String, String> values = new HashMap<String, String>();
values.put("name", "Steve");
values.put("port", "8080");
WifiP2pServiceInfo srvcInfo = WifiP2pDnsSdServiceInfo.newInstance(mMyDevice.deviceName, "_http._tcp", values);
manager.addLocalService(channel, srvcInfo, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
Toast.makeText(WiFiDirectActivity.this, "Local service added successfully",
Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(int reasonCode) {
Toast.makeText(WiFiDirectActivity.this, "Local service addition failed : " + reasonCode,
Toast.LENGTH_SHORT).show();
}
});
}
API to discover local services:
public void discoverService() {
manager.clearServiceRequests(channel, null);
DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
#Override
/* Callback includes:
* fullDomain: full domain name: e.g "printer._ipp._tcp.local."
* record: TXT record data as a map of key/value pairs.
* device: The device running the advertised service.
*/
public void onDnsSdTxtRecordAvailable(String fullDomain, Map record, WifiP2pDevice device) {
Log.d(TAG, "DnsSdTxtRecord available -" + record.toString());
}
};
DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() {
#Override
public void onDnsSdServiceAvailable(String instanceName, String registrationType, WifiP2pDevice resourceType) {
Log.d(TAG, "onBonjourServiceAvailable " + instanceName);
}
};
manager.setDnsSdResponseListeners(channel, servListener, txtListener);
WifiP2pDnsSdServiceRequest serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
manager.addServiceRequest(channel, serviceRequest, new ActionListener() {
#Override
public void onSuccess() {
// Success!
Log.d(TAG, "addServiceRequest success");
}
#Override
public void onFailure(int code) {
// Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY
Log.d(TAG, "addServiceRequest failure with code " + code);
}
});
manager.discoverServices(channel, new ActionListener() {
#Override
public void onSuccess() {
// Success!
Log.d(TAG, "discoverServices success");
}
#Override
public void onFailure(int code) {
// Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY
if (code == WifiP2pManager.P2P_UNSUPPORTED) {
Log.d(TAG, "P2P isn't supported on this device.");
} else {
Log.d(TAG, "discoverServices failure");
}
}
});
}
Note: manager & channel are initialized as
WifiP2pManager manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
Channel channel = manager.initialize(this, getMainLooper(), null);
WifiP2p (in general):
Some time ago I was developing an application with a pretty complex network connectivity system based on WifiP2p with Service Broadcasting/Discovery. And based on that experience I already wrote few posts here on SO about how difficult, wearing and problematic that is. Here are two of them (they are quite full of the inside knowledge I acquired about WifiP2p with Service Discovery, and WifiP2p itself):
Why is discovering peers for Android WifiDirect so unreliable
Wi-fi P2P. Inform all peers available of some event
I would advise you to read both of my answers (even though they are focused a bit more on the WifiP2p itself). They should give you some perspective on the things you should be looking for when working with the WifiP2p Service Discovery.
I can easily say that if you want to build an efficient, relatively reliable and robust WifiP2p connection system (especially with Service Discovery), you will have to work your ass off.
WifiP2p Service Discovery:
To better answer your exact question, I will tell you what I did (different from you) to make my Service Discovery work pretty reliably.
1. Broadcasting Service:
First of all: before registering your Service (with addLocalService method) you should use the WifiP2pManager's clearLocalServices method. And it is important, that you should only call addLocalService if the listener passed in the clearLocalServices returned with the onSuccess callback.
Although this sets up the broadcasting pretty nicely, I found that other nodes were not always able to detect the broadcasted service (especially when those nodes weren't already actively detecting services at the moment of registering your local Service - but they "joined" later). I couldn't find a way to fix this issue 100% reliably. And believe me I was trying probably everything WifiP2p-related. And no, the clearLocalServices-addLocalService sequence wasn't really giving satisfying results. Or more so: doing something different was working much better. What I decided to do, was after I successfully added local service (onSuccess callback from addLocalService), I started a Thread that would periodically call WifiP2pManager's method discoverPeers. That seemed to be forcing to rebroadcast all the service information.
So... basically the base of your broadcasting code should look more-less like this (bare in mind that every single piece of code I will post here is stripped-off of all "checks" if the network connectivity system is in the right state, you should design them yourself to fit your solution the best):
public void startBroadcastingService(){
mWifiP2pManager.clearLocalServices(mWifiP2pChannel, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
mWifiP2pManager.addLocalService(mWifiP2pChannel, mWifiP2pServiceInfo,
new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
// service broadcasting started
mServiceBroadcastingHandler
.postDelayed(mServiceBroadcastingRunnable,
SERVICE_BROADCASTING_INTERVAL);
}
#Override
public void onFailure(int error) {
// react to failure of adding the local service
}
});
}
#Override
public void onFailure(int error) {
// react to failure of clearing the local services
}
});
}
where the mServiceBroadcastingRunnable should be:
private Runnable mServiceBroadcastingRunnable = new Runnable() {
#Override
public void run() {
mWifiP2pManager.discoverPeers(mWifiP2pChannel, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
}
#Override
public void onFailure(int error) {
}
});
mServiceBroadcastingHandler
.postDelayed(mServiceBroadcastingRunnable, SERVICE_BROADCASTING_INTERVAL);
}
};
2. Discovering Service:
For the discovering of your service I used similar approach. Both with the setting up the discovering, and with trying to force "rediscovery" of services.
Setting up was performed with the sequence of the following three WifiP2pManager's methods:
removeServiceRequest, addServiceRequest, discoverServices
They were called in this exact order and a particular method (second or the third one to be exact) has been called only after the previous one had "returned" with the onSuccess callback.
The rediscovery of services was being performed with the intuitive method (just by repeating the mentioned sequence: removeServiceRequest -> addServiceRequest -> discoverServices).
The base of my code looked more-less like this (to start Service Discovery I would first call prepareServiceDiscovery() and then startServiceDiscovery()):
public void prepareServiceDiscovery() {
mWifiP2pManager.setDnsSdResponseListeners(mWifiP2pChannel,
new WifiP2pManager.DnsSdServiceResponseListener() {
#Override
public void onDnsSdServiceAvailable(String instanceName,
String registrationType, WifiP2pDevice srcDevice) {
// do all the things you need to do with detected service
}
}, new WifiP2pManager.DnsSdTxtRecordListener() {
#Override
public void onDnsSdTxtRecordAvailable(
String fullDomainName, Map<String, String> record,
WifiP2pDevice device) {
// do all the things you need to do with detailed information about detected service
}
});
mWifiP2pServiceRequest = WifiP2pDnsSdServiceRequest.newInstance();
}
private void startServiceDiscovery() {
mWifiP2pManager.removeServiceRequest(mWifiP2pChannel, mWifiP2pServiceRequest,
new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
mWifiP2pManager.addServiceRequest(mWifiP2pChannel, mWifiP2pServiceRequest,
new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
mWifiP2pManager.discoverServices(mWifiP2pChannel,
new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
//service discovery started
mServiceDiscoveringHandler.postDelayed(
mServiceDiscoveringRunnable,
SERVICE_DISCOVERING_INTERVAL);
}
#Override
public void onFailure(int error) {
// react to failure of starting service discovery
}
});
}
#Override
public void onFailure(int error) {
// react to failure of adding service request
}
});
}
#Override
public void onFailure(int reason) {
// react to failure of removing service request
}
});
}
the mServiceDiscoveringRunnable was just:
private Runnable mServiceDiscoveringRunnable = new Runnable() {
#Override
public void run() {
startServiceDiscovery();
}
};
All this made my system work quite well. It wasn't perfect yet, but with the lack of documentation on this subject I think I couldn't do much more to improve it.
If you test this approach, be sure to tell me how it works for you (or if it works for you ;) ).
if the problem is the detection of the service i believe that crearing group is the best way to make the device and service detectable but the if created group in the all devices then you cannot connect in direct.
but as wifi network.
i do it every day and it works.
I am having a problem with using the JmDNS library for an Android app.
Since Bonjour does not send periodic keep-alive messages, I am closing and opening a new JmDNS instance every 30 seconds, so that I can have the serviceAdded and serviceResolved functions called.
But in between these instance creations, if I switch off and on the Wifi, although the creation of the JmDNS instance succeeds, my serviceAdded and serviceResolved functions are not called.
My main doubts are:
1) Is it ok to re-create these instance again and again and therefore effect a kind-of polling? Or is there a better way?
2) If the network is switched off and on, is there something I need to do each time I create the JmDNS instance?
Here's a snippet of my code:
private void _startJmDnsService() throws IOException {
if (mJmdns != null) {
if (mJmDnsServiceListener != null) {
mJmdns.removeServiceListener(mBonjourServiceType, mJmDnsServiceListener);
mJmDnsServiceListener = null;
}
mJmdns.close();
mJmdns = null;
}
Log.d(TAG, "starting JmDNS");
mJmdns = JmDNS.create(getInet4Address());
mJmDnsServiceListener = new ServiceListener() {
public void serviceResolved(ServiceEvent ev) {
ServiceInfo serviceInfo = ev.getInfo();
Log.w(TAG, "serviceResolved for device " + serviceInfo.getName());
}
public void serviceRemoved(ServiceEvent ev) {
ServiceInfo serviceInfo = ev.getInfo();
Log.w(TAG, "serviceRemoved for device " + serviceInfo.getName());
CDevice.removeDevice(deviceId);
}
public void serviceAdded(ServiceEvent event) {
// Required to force serviceResolved to be called again
// (after the first search)
ServiceInfo serviceInfo = event.getInfo();
Log.w(TAG, "serviceAdded for device " + serviceInfo.getName());
mJmdns.requestServiceInfo(event.getType(), event.getName(), 1);
}
};
mJmdns.addServiceListener(mBonjourServiceType, mJmDnsServiceListener);
Log.w(TAG, "mJmdns Service Listener added!");
}
For anyone that might have a similar problem. There was nothing in the JmDNS library that had a problem. In my code, I was closing the multicast socket between instance calls, which was creating a problem.
Can somebody explain the error messages that can be passed into the callbacks i.e.
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode)
I've seen error code 3 before but have no idea what it means. The documentation seems flakey at best...
Cheers,
I worked around this by calling resolveService again. It may fail a couple of times in a row, but eventually resolves.
#Override
public void onServiceFound(NsdServiceInfo serviceInfo) {
Log.d(TAG, "Service found: "+ serviceInfo);
if (!serviceInfo.getServiceType().equals(SERVICE_TYPE)){
Log.d(TAG, "Unknown service type: " + serviceInfo.getServiceType());
} else if (serviceInfo.getServiceName().equals(mServiceName)){
Log.d(TAG, "Same machine");
} else {
startResolveService(serviceInfo);
}
}
Rather than call resolveService from within onServiceFound, I call a separate declaration of it:
private void startResolveService(NsdServiceInfo serviceInfo){
NsdManager.ResolveListener newResolveListener = new NsdManager.ResolveListener() {
#Override
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
Log.e(TAG, "Resolve Failed: " + serviceInfo + "\tError Code: " + errorCode);
switch (errorCode) {
case NsdManager.FAILURE_ALREADY_ACTIVE:
Log.e(TAG, "FAILURE_ALREADY_ACTIVE");
// Just try again...
startResolveService(serviceInfo);
break;
case NsdManager.FAILURE_INTERNAL_ERROR:
Log.e(TAG, "FAILURE_INTERNAL_ERROR");
break;
case NsdManager.FAILURE_MAX_LIMIT:
Log.e(TAG, "FAILURE_MAX_LIMIT");
break;
}
}
#Override
public void onServiceResolved(NsdServiceInfo serviceInfo) {
Log.i(TAG, "Service Resolved: " + serviceInfo);
mLocatedServices.add(serviceInfo);
reportNewService();
}
};
mNsdManager.resolveService(serviceInfo, newResolveListener);
}
When multiple services are discovered nearly-instantaneously, the first one gets resolved, then you get FAILURE_ALREADY_ACTIVE once or twice more, then the next service gets resolved, and so on.
Had the same question and got the answer from NsdManager sources:
/**
* Failures are passed with {#link RegistrationListener#onRegistrationFailed},
* {#link RegistrationListener#onUnregistrationFailed},
* {#link DiscoveryListener#onStartDiscoveryFailed},
* {#link DiscoveryListener#onStopDiscoveryFailed} or {#link ResolveListener#onResolveFailed}.
*
* Indicates that the operation failed due to an internal error.
*/
public static final int FAILURE_INTERNAL_ERROR = 0;
/**
* Indicates that the operation failed because it is already active.
*/
public static final int FAILURE_ALREADY_ACTIVE = 3;
/**
* Indicates that the operation failed because the maximum outstanding
* requests from the applications have reached.
*/
public static final int FAILURE_MAX_LIMIT = 4;
edited:
Actually it is mentioned in developer documentation:
http://developer.android.com/reference/android/net/nsd/NsdManager.html
I was a bit confused by this also...
Here's what's happening. When you are discovering services, you will often get the exact same service a few times in the same millisecond.
if you log the output from NsdManager.DiscoveryListener.onServiceFound(NsdServiceInfo service) you'll see this.
Now, you probably resolve the service using mNsdManager.resolveService(service, mResolveListener). Only the first attempt is going to start resolving, the others will give FAILURE_ALREADY_ACTIVE. (At least until the first is completed)
I met this problem two days....and i solve it...
first time i do resolveService(service, mResolveListener);
no callback
and i do again same service, resolveService(service, mResolveListener);
the callback is error code 3
At least I found ,because my server name is "III...."
I take out double quotes and it's work
hope it's can help you.
I found that reusing the same listener with a semaphore gave the best result.
if (semaphore.tryAcquire()) {
mNsdManager.resolveService(service, resolveListener);
}
Using tryAcquire as we do not wan't to block onServiceFound which will lock it completely. With this approach we eventually get all services on the network to show up.