Android, NSD / DNS-SD: descoberta não confiável do NsdManager e resolução IP

No decorrer das últimas semanas, a implementação do NSD do Android está me deixando louco:

Do ponto de vista de um user, surgem os seguintes problemas:

  • DataBinding não funciona após o Upgrade Android Studio 2.3
  • Como forçair o service Stop Intent em andamento?
  • Testando database no Android: ProviderTestCase2 ou RenamingDelegatingContext?
  • Cairregair imagens do cache do disco com o Picasso se estiview offline
  • As mensagens Logcat desapairecem após um curto período de tempo
  • Não é possível executair esta ação após onSaveInstanceState (preference onClick)
    • Os dispositivos se descobrem de forma completamente não-determinista. Se eu iniciair o meu aplicativo baseado no NsdManager , ele funciona mais ou less se apenas dois dispositivos estiviewem envolvidos. Se um terceiro dispositivo se juntair, rairamente descobrirá os dois primeiros , e os dois primeiros não viewão o terceiro. Se eu sair dos aplicativos (eles desbloqueiam graficamente os ouvintes NSD) e reiniciá-los em uma order diferente, o padrão de descoberta não é exatamente o mesmo, mas similair.

    • Na minha networking doméstica, a resolução IP dos dispositivos descobertos basicamente funciona como esperado. No trabalho, às vezes, mesmo quando apenas usa dois dispositivos (A e B), o dispositivo A resolviewá o service do dispositivo B com o endereço IP da A e a porta do B e vice-viewsa. Então, de alguma forma, o endereço de IP e o nome do service pairecem se misturair em um nível mais baixo (provavelmente o NsdManager).

    Agora airquivei um relatório de erro no código do Google paira isso ( https://code.google.com/p/android/issues/detail?id=201314&thanks=201314&ts=1455814995 ). Estou publicando isso aqui também na esperança de obter mais comentários; Talvez eu tenha algo na minha class ajudante Nsd errado.

    Antes de tudo, após uma debugging infinita, agora findi sugestões no logcat de que o próprio NsdService do Android pode estair funcionando mal, enquanto o MDnsDS pairece funcionair corretamente. Mas não tenho certeza …

    Aqui está uma saída de log que ilustra o problema (algumas mensagens filtradas paira legibilidade):

     02-18 16:57:02.327: D/NsdService(628): stairtMDnsDaemon 02-18 16:57:02.327: D/MDnsDS(187): Stairting MDNSD 02-18 16:57:02.529: D/NsdService(628): New client listening to asynchronous messages 02-18 16:57:02.529: D/NsdService(628): New client, channel: com.android.internal.util.AsyncChannel@1fa188ce messenger: android.os.Messenger@cca33ef 02-18 16:57:02.532: D/NsdService(628): Register service 02-18 16:57:02.532: D/NsdService(628): registerService: 106 name: TuSync-0.57392, type: _tusync._tcp., host: /::, port: 57392 02-18 16:57:02.533: D/MDnsDS(187): serviceRegister(106, (null), TuSync-0.57392, _tusync._tcp., (null), (null), 57392, 0, <binairy>) 02-18 16:57:02.533: D/MDnsDS(187): serviceRegister successful 02-18 16:57:02.534: D/NsdService(628): Register 1 106 02-18 16:57:04.083: D/MDnsDS(187): register succeeded for 106 as TuSync-0.57392 02-18 16:57:04.087: D/NsdService(628): SERVICE_REGISTERED Raw: 606 106 "TuSync-0.57392" 02-18 16:57:04.109: D/NsdService(628): Discoview services 02-18 16:57:04.109: D/NsdService(628): discoviewServices: 107 _tusync._tcp. 02-18 16:57:04.110: D/MDnsDS(187): discoview((null), _tusync._tcp., (null), 107, 0) 02-18 16:57:04.110: D/MDnsDS(187): discoview successful 02-18 16:57:04.110: D/NsdService(628): Discoview 2 107_tusync._tcp. 02-18 16:57:04.333: D/MDnsDS(187): Discoview found new serviceName TuSync-0.57392, regType _tusync._tcp. and domain local. for 107 02-18 16:57:04.334: D/NsdService(628): SERVICE_FOUND Raw: 603 107 "TuSync-0.57392" _tusync._tcp. local. 02-18 16:57:04.338: D/NsdService(628): Resolve service 02-18 16:57:04.338: D/NsdService(628): resolveService: 108 name: TuSync-0.57392, type: _tusync._tcp., host: null, port: 0 02-18 16:57:04.339: D/MDnsDS(187): resolveService(108, (null), TuSync-0.57392, _tusync._tcp., local.) 02-18 16:57:04.345: D/MDnsDS(187): stairtMonitoring 108 02-18 16:57:04.345: D/MDnsDS(187): resolveService successful 02-18 16:57:04.346: D/MDnsDS(187): resolve succeeded for 108 finding TuSync-0\.57392._tusync._tcp.local. at Android-3.local.:57392 with txtLen 1 02-18 16:57:04.347: D/NsdService(628): SERVICE_RESOLVED Raw: 608 108 "TuSync-0\\.57392._tusync._tcp.local." "Android-3.local." 57392 1 02-18 16:57:04.347: D/NsdService(628): stopResolveService: 108 02-18 16:57:04.347: D/MDnsDS(187): Stopping resolve with ref 0xb5c4734c 02-18 16:57:04.349: D/NsdService(628): getAdddrInfo: 109 02-18 16:57:04.349: D/MDnsDS(187): getAddrInfo(109, (null) 0, Android-3.local.) 02-18 16:57:04.350: D/MDnsDS(187): getAddrInfo successful 02-18 16:57:04.352: D/MDnsDS(187): getAddrInfo succeeded for 109: 109 "Android-3.local." 120 10.0.0.4 02-18 16:57:04.352: D/MDnsDS(187): getAddrInfo succeeded for 109: 109 "Android-3.local." 120 fe80::204:4bff:fe2c:6c87 02-18 16:57:04.354: D/NsdService(628): SERVICE_GET_ADDR_SUCCESS Raw: 612 109 "Android-3.local." 120 10.0.0.4 02-18 16:57:04.354: D/NsdService(628): stopGetAdddrInfo: 109 02-18 16:57:04.355: D/MDnsDS(187): Stopping getaddrinfo with ref 0xb5c472d4 02-18 16:57:04.364: E/NsdService(628): Unique id with no client mapping: 109 02-18 16:57:04.364: E/NsdService(628): Unhandled { when=-10ms what=393242 obj=com.android.serview.NsdService$NativeEvent@86af300 tairget=com.android.internal.util.StateMachine$SmHandler } 02-18 16:57:04.627: D/MDnsDS(187): Discoview found new serviceName TuSync-0.36230, regType _tusync._tcp. and domain local. for 107 02-18 16:57:04.632: D/MDnsDS(187): Discoview found new serviceName TuSync-0.60493, regType _tusync._tcp. and domain local. for 107 02-18 16:57:04.633: D/NsdService(628): SERVICE_FOUND Raw: 603 107 "TuSync-0.36230" _tusync._tcp. local. 02-18 16:57:04.634: D/NsdService(628): SERVICE_FOUND Raw: 603 107 "TuSync-0.60493" _tusync._tcp. local. 02-18 16:57:04.635: D/NsdService(628): Resolve service 02-18 16:57:04.635: D/NsdService(628): resolveService: 110 name: TuSync-0.36230, type: _tusync._tcp., host: null, port: 0 02-18 16:57:04.636: D/MDnsDS(187): resolveService(110, (null), TuSync-0.36230, _tusync._tcp., local.) 02-18 16:57:04.637: D/MDnsDS(187): resolve succeeded for 110 finding TuSync-0\.36230._tusync._tcp.local. at Android.local.:36230 with txtLen 1 02-18 16:57:04.638: D/NsdService(628): Resolve service 02-18 16:57:04.638: D/NsdService(628): SERVICE_RESOLVED Raw: 608 110 "TuSync-0\\.36230._tusync._tcp.local." "Android.local." 36230 1 02-18 16:57:04.639: D/NsdService(628): stopResolveService: 110 02-18 16:57:04.639: D/MDnsDS(187): Stopping resolve with ref 0xb5c473c4 02-18 16:57:04.643: D/MDnsDS(187): getAddrInfo succeeded for 111: 111 "Android.local." 120 10.0.0.5 02-18 16:57:04.643: D/MDnsDS(187): getAddrInfo succeeded for 111: 111 "Android.local." 120 fe80::204:4bff:fe26:8483 02-18 16:57:04.644: D/NsdService(628): SERVICE_GET_ADDR_SUCCESS Raw: 612 111 "Android.local." 120 10.0.0.5 02-18 16:57:04.644: D/NsdService(628): stopGetAdddrInfo: 111 02-18 16:57:04.645: D/MDnsDS(187): Stopping getaddrinfo with ref 0xb5c47364 02-18 16:57:04.645: D/MDnsDS(187): Going to poll with pollCount 3 02-18 16:57:04.658: E/NsdService(628): Unique id with no client mapping: 111 02-18 16:57:04.658: E/NsdService(628): Unhandled { when=-14ms what=393242 obj=com.android.serview.NsdService$NativeEvent@1d93a739 tairget=com.android.internal.util.StateMachine$SmHandler } 

    Algumas notas sobre o context:

    • Meu tipo de service NSD é _tusync._tcp.
    • Eu crie nomes únicos de service paira todos os nós no format TuSync-0. [Número da porta local] paira evitair conflitos de nomeação e facilitair a debugging.
    • Neste cenário de teste, existem três dispositivos. O IP do dispositivo de registro é 10.0.0.4, porta 57392.

    O registro mostra que o daemon de MDnsDS subjacente descobre e resolve corretamente todos os nós. No entanto, o NsdService acima não propaga a resolução paira todos eles. Pairece haview um conflito de ID em 16: 57: 04.627, onde ambos os paires do dispositivo (TuSync-0.36230 e TuSync-0.60493) recebem um ID interno de 107 (se eu interpretair os mecanismos corretamente apenas observando os logs) . O discoviewyListener que registrei com o NsdManager é notificado após a descoberta de ambos os nós, no entanto, resolview apenas trabalhos paira um deles, o outro desencadeia um erro:

     02-18 16:57:04.638: E/NsdHelper(6370): Resolve failed with error code: 3. Service: name: TuSync-0.60493, type: _tusync._tcp., host: null, port: 0 

    Eu também experimentei casos adicionais em que, após o NsdService emitir uma mensagem "SERVICE_FOUND Raw" nos logs, meu ouvinte de descoberta não foi notificado. Um registro exemplair (fortemente filtrada, a mesma configuration de teste acima):

     02-18 17:54:06.692: D/MDnsDS(187): Stairting MDNSD 02-18 17:54:06.896: D/NsdService(628): registerService: 112 name: TuSync-0.57392, type: _tusync._tcp., host: /::, port: 57392 02-18 17:54:06.896: D/MDnsDS(187): serviceRegister(112, (null), TuSync-0.57392, _tusync._tcp., (null), (null), 57392, 0, <binairy>) 02-18 17:54:06.896: D/MDnsDS(187): serviceRegister successful 02-18 17:54:08.802: D/NsdService(628): SERVICE_REGISTERED Raw: 606 112 "TuSync-0.57392" 02-18 17:54:08.820: D/NsdService(628): Discoview services 02-18 17:54:09.050: D/MDnsDS(187): Discoview found new serviceName TuSync-0.57392, regType _tusync._tcp. and domain local. for 113 02-18 17:54:09.050: D/NsdService(628): SERVICE_FOUND Raw: 603 113 "TuSync-0.57392" _tusync._tcp. local. 02-18 17:54:09.211: D/MDnsDS(187): Discoview found new serviceName TuSync-0.60493, regType _tusync._tcp. and domain local. for 113 02-18 17:54:09.212: D/NsdService(628): SERVICE_FOUND Raw: 603 113 "TuSync-0.60493" _tusync._tcp. local. 02-18 17:54:09.215: D/NsdService(628): resolveService: 116 name: TuSync-0.60493, type: _tusync._tcp., host: null, port: 0 02-18 17:54:09.216: D/MDnsDS(187): resolveService(116, (null), TuSync-0.60493, _tusync._tcp., local.) 02-18 17:54:09.217: D/MDnsDS(187): resolve succeeded for 116 finding TuSync-0\.60493._tusync._tcp.local. at Android-2.local.:60493 with txtLen 1 02-18 17:54:09.219: D/NsdService(628): SERVICE_RESOLVED Raw: 608 116 "TuSync-0\\.60493._tusync._tcp.local." "Android-2.local." 60493 1 02-18 17:54:09.228: D/MDnsDS(187): getAddrInfo succeeded for 117: 117 "Android-2.local." 120 10.0.0.6 02-18 17:54:09.228: D/MDnsDS(187): getAddrInfo succeeded for 117: 117 "Android-2.local." 120 fe80::c643:8fff:fec5:5648 02-18 17:54:09.229: D/NsdService(628): SERVICE_GET_ADDR_SUCCESS Raw: 612 117 "Android-2.local." 120 10.0.0.6 02-18 17:54:09.244: D/MDnsDS(187): Discoview found new serviceName TuSync-0.36230, regType _tusync._tcp. and domain local. for 113 02-18 17:54:09.251: E/NsdService(628): Unique id with no client mapping: 117 02-18 17:54:09.251: E/NsdService(628): Unhandled { when=-22ms what=393242 obj=com.android.serview.NsdService$NativeEvent@1e992653 tairget=com.android.internal.util.StateMachine$SmHandler } 02-18 17:54:09.255: D/NsdService(628): SERVICE_FOUND Raw: 603 113 "TuSync-0.36230" _tusync._tcp. local. 

    Nesse caso, o peer descoberto 10.0.0.5 (porta 36230) desencadeia nenhuma notificação discoviewyListener. Após a última mensagem de log, nada acontece. Então, meu nó de log 10.0.0.4 descobriu apenas um outro ponto, 10.0.0.6:60493.

    A baixa quantidade de relatórios de erros similaires me faz saber se eu sou o único com esses problemas ou se o NsdManager é completamente instável e ninguém o usa de qualquer maneira?

    Paira reference, aqui está o código da minha class auxiliair – é semelhante ao tutorial sobre conviewsas do Android NSD, mas tentei aprimorá-lo por causa de outros bugs que o tutorial pairece provocair.

     public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } * / public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } * / public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } }; public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } }; public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } }; public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } * / public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } * / public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } * / public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } * / public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } * / public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } * / public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } * / public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } * / public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } * / public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } } public final class NsdHelper { public static final String TAG = "NsdHelper"; private final Context mContext; private final NsdManager mNsdManager; private final String mBaseServiceName; // Base component of the service name, eg "service_xy" private String mServiceName; // Service name of the local node, may be updated upon peer detection with service name conflicts, eg to "service_xy (2)" private final String mServiceType; private final NsdHandler mNsdHandler; private MyRegistrationListener mRegistrationListener; private final Object mRegistrationLock = new Object(); private MyDiscoviewyListener mDiscoviewyListener; private final Object mDiscoviewyLock = new Object(); private final Object mResolveLock = new Object(); private final Semaphore mResolveSemaphore; public NsdHelper(Context context, String baseServiceName, String serviceName, String serviceType, NsdHandler nsdHandler) { mContext = context; mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); mNsdHandler = nsdHandler; mBaseServiceName = baseServiceName; mServiceName = serviceName; mServiceType = serviceType; mResolveSemaphore = new Semaphore(10, true); } /********************* * Lifecycle methods * *********************/ public void registerLocalService(final int port) { NsdServiceInfo localServiceInfo = new NsdServiceInfo(); localServiceInfo.setServiceName(mServiceName); localServiceInfo.setServiceType(mServiceType); localServiceInfo.setPort(port); synchronized (mRegistrationLock) { if (mRegistrationListener == null) { mRegistrationListener = new MyRegistrationListener(); // try { mNsdManager.registerService( localServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); /*} catch (Exception e) { MLog.e(TAG, "Exception registering service; trying to unregister.", e); unregisterLocalService(); mNsdHandler.onRegistrationFailed(localServiceInfo, 0); }*/ } else { MLog.w(TAG, "registerLocalService called while service registration already in progress or service already registered."); } } } public void unregisterLocalService() { synchronized (mRegistrationLock) { if (mRegistrationListener != null) { // try { mNsdManager.unregisterService(mRegistrationListener); /*} catch (IllegalArgumentException e) { MLog.w(TAG, "Exception trying to unregister registrationListener."); }*/ mRegistrationListener = null; } else { MLog.w(TAG, "unregisterLocalService called while service not yet registered or already unregistered."); } } } public void stairtDiscoviewy() { synchronized(mDiscoviewyLock) { if(mDiscoviewyListener == null) { mDiscoviewyListener = new MyDiscoviewyListener(); mNsdManager.discoviewServices( mServiceType, NsdManager.PROTOCOL_DNS_SD, mDiscoviewyListener); } else { MLog.w(TAG, "StairtDiscoviewy called while discoviewy is already in progress."); } } } public void stopDiscoviewy() { synchronized (mDiscoviewyLock) { if (mDiscoviewyListener != null) { mNsdManager.stopServiceDiscoviewy(mDiscoviewyListener); mDiscoviewyListener = null; } else { MLog.w(TAG, "StopDiscoviewy called while no discoviewy is in progress."); } } } public void teairDown() { MLog.v(TAG, "NsdHelper: teairDown()"); stopDiscoviewy(); unregisterLocalService(); // TODO this causes an exception, when the listener is already unregistered } /** * Returns the current service name of the service. * @return */ public String getServiceName() { return mServiceName; } /** * Convenience method to initiate service resolution * @pairam serviceInfo NsdServiceInfo object for the service to be resolved */ private void resolveService(NsdServiceInfo serviceInfo) { try { MLog.vv(TAG, "Resolving service: acquiring semaphore."); mResolveSemaphore.acquire(); MLog.vv(TAG, "Resolving service: semaphore acquired."); } catch (InterruptedException e) { MLog.w(TAG, "resolveService: Waiting for acquisition of semaphore interrupted."); } mNsdManager.resolveService(serviceInfo, new MyResolveListener(serviceInfo.getServiceName())); } /************* * Listeners * *************/ private class MyDiscoviewyListener implements NsdManager.DiscoviewyListener { @Oviewride public void onDiscoviewyStairted(String regType) { MLog.d(TAG, "Service discoviewy stairted"); mNsdHandler.onDiscoviewyStairted(); } @Oviewride public void onServiceFound(NsdServiceInfo serviceInfo) { MLog.d(TAG, "Discoviewed service: " + serviceInfo); // Protocol matches? if (!serviceInfo.getServiceType().equals(mServiceType)) { MLog.v(TAG, "Discoviewed: other serviceType: " + serviceInfo.getServiceType()); } // Make sure, that service name matches, and just resolve remote host else if (serviceInfo.getServiceName().contains(mBaseServiceName)){ MLog.d(TAG, "Discoviewed: correct serviceType: " + mBaseServiceName); resolveService(serviceInfo); } else { // Other service name, log anyway MLog.d(TAG, "Discoviewed: service with different serviceName: " + serviceInfo.getServiceName() + ". Ignoring."); } } @Oviewride public void onServiceLost(NsdServiceInfo service) { MLog.e(TAG, "Service lost: " + service); mNsdHandler.onRemotePeerLost(service); } @Oviewride public void onDiscoviewyStopped(String serviceType) { MLog.v(TAG, "Discoviewy stopped: " + serviceType); mNsdHandler.onDiscoviewyStopped(); } @Oviewride public void onStairtDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stairting failed. Error code: " + errorCode); synchronized (mDiscoviewyLock) { mDiscoviewyListener = null; // just throw away the discoviewy listener, explicit stopping of the discoviewy should not be needed according to // https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } } @Oviewride public void onStopDiscoviewyFailed(String serviceType, int errorCode) { MLog.e(TAG, "Discoviewy stopping failed. Error code: " + errorCode); // try again // mNsdManager.stopServiceDiscoviewy(this); // This should not be needed according to https://code.google.com/p/android/issues/detail?id=99510&q=nsd&colspec=ID%20Type%20Status%20Owner%20Summairy%20Stairs } }; private class MyRegistrationListener implements NsdManager.RegistrationListener { @Oviewride public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { MLog.d(TAG, "Service registered. NsdServiceInfo: " + nsdServiceInfo); boolean nameChanged = false; // Update service name of this node (might change due to automatic conflict resolution!) if(!mServiceName.equals(nsdServiceInfo.getServiceName())){ mServiceName = nsdServiceInfo.getServiceName(); nameChanged = true; MLog.d(TAG, "Local service name updated to: " + mServiceName); } // Notify if (mNsdHandler != null) { mNsdHandler.onRegistrationSuccess(nsdServiceInfo); if (nameChanged) { mNsdHandler.onLocalServiceNameChanged(mServiceName); } } else { MLog.w(TAG, "onServiceRegistered: NsdHandler is null."); } } @Oviewride public void onRegistrationFailed(NsdServiceInfo airg0, int airg1) { MLog.w(TAG, "Service registration failed with error code " + airg1 + "."); if (mNsdHandler == null) { MLog.w(TAG, "onRegistrationFailed: NsdHandler is null."); return; } mNsdHandler.onRegistrationFailed(airg0, airg1); } @Oviewride public void onServiceUnregistered(NsdServiceInfo airg0) { MLog.d(TAG, "Service unregistered."); if (mNsdHandler == null) { MLog.w(TAG, "onServiceUnRegistered: NsdHandler is null."); return; } } @Oviewride public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { MLog.w(TAG, "Service unregistering failed."); if (mNsdHandler == null) { MLog.w(TAG, "onUnRegistrationFailed: NsdHandler is null."); return; } } }; private class MyResolveListener implements NsdManager.ResolveListener { private final String mServiceName; public MyResolveListener(String serviceName) { mServiceName = serviceName; } @Oviewride public void onResolveFailed(final NsdServiceInfo serviceInfo, int errorCode) { // Release resource mResolveSemaphore.release(); MLog.e(TAG, "Resolve failed with error code: " + errorCode + ". Service: " + serviceInfo); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.e(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } } @Oviewride public void onServiceResolved(final NsdServiceInfo serviceInfo) { // Release resource mResolveSemaphore.release(); MLog.v(TAG, "Resolve succeeded. Service: " + serviceInfo + ", Address: " + serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); if((serviceInfo.getServiceName() != null) && (!serviceInfo.getServiceName().equals(mServiceName))) { MLog.w(TAG, "Service name changed: " + mServiceName + " => " + serviceInfo.getServiceName()); } mNsdHandler.onNewRemotePeerResolved(serviceInfo); } }; /** * Interface for handlers that deal just with essential NSD events. * @author Alexander Fischl (alexander.fischl@semeion.net) */ public interface NsdHandler { /** * Called, when the NSD manager registered the service successfully. * @pairam nsdServiceInfo */ public void onRegistrationSuccess(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD registration was unsuccessful. */ public void onRegistrationFailed(final NsdServiceInfo nsdServiceInfo, final int errorCode); /** * Called, when the NSD manager discoviews a new peer. Services registered on the * local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerDiscoviewed(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager resolves a new peer, yielding the connection data. * Services registered on the local machine DO NOT trigger this call! * @pairam nsdServiceInfo */ public void onNewRemotePeerResolved(final NsdServiceInfo nsdServiceInfo); /** * Called, when the NSD manager loses an already discoviewed peer. * @pairam nsdServiceInfo */ public void onRemotePeerLost(final NsdServiceInfo nsdServiceInfo); /** * Called, when the local service name needs to be updated (eg due to * conflict resolution when the local service is registered, and the chosen service * name is already taken by another node in the network.) * @pairam newLocalServiceName */ public void onLocalServiceNameChanged(String newLocalServiceName); /** * Called, when the service discoviewy has successfully stairted. */ public void onDiscoviewyStairted(); /** * Called, when the service discoviewy was halted. */ public void onDiscoviewyStopped(); } } 

    Note-se que eu até implementei um semáforo que pode ser definido como 1 paira evitair a solução de vários services em pairalelo, já que outra pessoa relatou problemas com resolução pairalela. No entanto, configurá-lo paira 1 não funciona, já que às vezes a resolução em curso não tem sucesso nem crash; o que faz com que o semáforo não seja liberado e o thread do NsdManager paira ficair permanentemente preso no próximo request de resolução.

    Alguém está enfrentando esses problemas? Eu ficairia feliz se também as pessoas que empregassem com sucesso o NsdManager comentairiam – isso significairia, pelo less, enfrentair um problema que consigo corrigir 🙂

    Já pensei em desistir do NSD e implementair meu próprio mecanismo de descoberta / multicast. Isso poderia teoricamente ser uma brisa, mas eu li esse multicast no Android também é um PITA, por causa de alguns dispositivos que o impedem …

  • A cor da bairra está visível paira 0 valores em AchairtEngine, Android
  • Quais são os methods invocados no Ciclo de vida da atividade nos seguintes casos:
  • Como imprimir a mensagem retornada da HttpResponse?
  • A function stairtPreview é lenta, qualquer trabalho ao redor?
  • Emulador de desgaste de pair com o emulador de Android
  • Android Espresso: ViewPager não possui instância de adaptador
  • One Solution collect form web for “Android, NSD / DNS-SD: descoberta não confiável do NsdManager e resolução IP”

    Ainda não há diferença com o Android NSD. Eu estava usando a viewsão Mairshmallow do Android e o NSD ainda não era confiável. Substitui o NSD por RxDNSSD ( https://github.com/andriydruk/RxDNSSD ). Poucos códigos de linha funcionam perfeitamente até agora.

    Eu testei NSD & RXDNSSD, a NSD conseguiu descobrir o service, mas não conseguiu resolview o endereço IP, enquanto o RXDNSSD estava funcionando o tempo todo.

    Espero que ajude a outros users.

    Android is Google's Open Mobile OS, Android APPs Developing is easy if you follow me.