Bug 1193605 - Part 5: separate different discovery/registration requests by UUIDs; r=rnewman
authorLiang-Heng Chen <xeonchen@mozilla.com>
Wed, 09 Sep 2015 08:30:00 +0200
changeset 294723 bd4e19973ec21cad1bb306701130eaf1e60035c5
parent 294722 62e37d61a0961d6700518fa35c091dce7873f87c
child 294724 190b9bfc9ae281919dba6c036b641c3c7d289ae1
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewman
bugs1193605
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1193605 - Part 5: separate different discovery/registration requests by UUIDs; r=rnewman
mobile/android/base/mdns/MulticastDNSManager.java
mobile/android/modules/MulticastDNS.jsm
--- a/mobile/android/base/mdns/MulticastDNSManager.java
+++ b/mobile/android/base/mdns/MulticastDNSManager.java
@@ -22,16 +22,17 @@ import android.content.Context;
 import android.net.nsd.NsdManager;
 import android.net.nsd.NsdServiceInfo;
 import android.support.annotation.UiThread;
 import android.util.Log;
 
 import java.net.InetAddress;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * This class is the bridge between XPCOM mDNS module and NsdManager.
  *
  * @See nsIDNSServiceDiscovery.idl
  */
 public abstract class MulticastDNSManager {
     protected static final String LOGTAG = "GeckoMDNSManager";
@@ -105,76 +106,88 @@ class MulticastDNSEventManager {
                 "NsdManager:UnregisterService",
                 "NsdManager:ResolveService");
     }
 }
 
 class NsdMulticastDNSManager extends MulticastDNSManager implements NativeEventListener {
     private final NsdManager nsdManager;
     private final MulticastDNSEventManager mEventManager;
-    private DiscoveryListener mDiscoveryListener = null;
-    private RegistrationListener mRegistrationListener = null;
+    private Map<String, DiscoveryListener> mDiscoveryListeners = null;
+    private Map<String, RegistrationListener> mRegistrationListeners = null;
 
     @TargetApi(16)
     public NsdMulticastDNSManager(final Context context) {
         nsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
         mEventManager = new MulticastDNSEventManager(this);
+        mDiscoveryListeners = new ConcurrentHashMap<String, DiscoveryListener>();
+        mRegistrationListeners = new ConcurrentHashMap<String, RegistrationListener>();
     }
 
     @Override
     public void init() {
         mEventManager.init();
     }
 
     @Override
     public void tearDown() {
+        mDiscoveryListeners.clear();
+        mRegistrationListeners.clear();
+
         mEventManager.tearDown();
     }
 
     @Override
     public void handleMessage(final String event, final NativeJSObject message, final EventCallback callback) {
         Log.v(LOGTAG, "handleMessage: " + event);
 
         switch (event) {
-            case "NsdManager:DiscoverServices":
-                if (mDiscoveryListener != null) {
-                    mDiscoveryListener.stopServiceDiscovery(null);
-                }
-                mDiscoveryListener = new DiscoveryListener(nsdManager);
-                mDiscoveryListener.discoverServices(message.getString("serviceType"), callback);
+            case "NsdManager:DiscoverServices": {
+                DiscoveryListener listener = new DiscoveryListener(nsdManager);
+                listener.discoverServices(message.getString("serviceType"), callback);
+                mDiscoveryListeners.put(message.getString("uniqueId"), listener);
                 break;
-            case "NsdManager:StopServiceDiscovery":
-                if (mDiscoveryListener != null) {
-                    mDiscoveryListener.stopServiceDiscovery(callback);
-                    mDiscoveryListener = null;
+            }
+            case "NsdManager:StopServiceDiscovery": {
+                String uuid = message.getString("uniqueId");
+                DiscoveryListener listener = mDiscoveryListeners.remove(uuid);
+                if (listener == null) {
+                    Log.e(LOGTAG, "DiscoveryListener " + uuid + " was not found.");
+                    return;
                 }
+                listener.stopServiceDiscovery(callback);
                 break;
-            case "NsdManager:RegisterService":
-                if (mRegistrationListener != null) {
-                    mRegistrationListener.unregisterService(null);
-                }
-                mRegistrationListener = new RegistrationListener(nsdManager);
-                mRegistrationListener.registerService(message.getInt("port"),
+            }
+            case "NsdManager:RegisterService": {
+                RegistrationListener listener = new RegistrationListener(nsdManager);
+                listener.registerService(message.getInt("port"),
                         message.optString("serviceName", android.os.Build.MODEL),
                         message.getString("serviceType"),
                         parseAttributes(message.optObjectArray("attributes", null)),
                         callback);
+                mRegistrationListeners.put(message.getString("uniqueId"), listener);
                 break;
-            case "NsdManager:UnregisterService":
-                if (mRegistrationListener != null) {
-                    mRegistrationListener.unregisterService(callback);
-                    mRegistrationListener = null;
+            }
+            case "NsdManager:UnregisterService": {
+                String uuid = message.getString("uniqueId");
+                RegistrationListener listener = mRegistrationListeners.remove(uuid);
+                if (listener == null) {
+                    Log.e(LOGTAG, "RegistrationListener " + uuid + " was not found.");
+                    return;
                 }
+                listener.unregisterService(callback);
                 break;
-            case "NsdManager:ResolveService":
+            }
+            case "NsdManager:ResolveService": {
                 (new ResolveListener(nsdManager)).resolveService(message.getString("serviceName"),
                         message.getString("serviceType"),
                         callback);
                 break;
-        };
+            }
+        }
     }
 
     private Map<String, String> parseAttributes(final NativeJSObject[] jsobjs) {
         if (jsobjs == null || jsobjs.length == 0 || !Versions.feature21Plus) {
             return null;
         }
 
         Map<String, String> attributes = new HashMap<String, String>(jsobjs.length);
@@ -248,95 +261,109 @@ class DiscoveryListener implements NsdMa
     private EventCallback mStopCallback = null;
 
     DiscoveryListener(final NsdManager nsdManager) {
         this.nsdManager = nsdManager;
     }
 
     public void discoverServices(final String serviceType, final EventCallback callback) {
         synchronized (this) {
-            if (mStartCallback != null) {
-                throw new RuntimeException("Previous operation is not finished");
-            }
             mStartCallback = callback;
         }
         nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, this);
     }
 
     public void stopServiceDiscovery(final EventCallback callback) {
         synchronized (this) {
-            if (mStopCallback != null) {
-                throw new RuntimeException("Previous operation is not finished");
-            }
             mStopCallback = callback;
         }
         nsdManager.stopServiceDiscovery(this);
     }
 
     @Override
     public synchronized void onDiscoveryStarted(final String serviceType) {
         Log.d(LOGTAG, "onDiscoveryStarted: " + serviceType);
-        if (mStartCallback == null) {
+
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStartCallback;
+        }
+
+        if (callback == null) {
             return;
         }
-        mStartCallback.sendSuccess(serviceType);
-        mStartCallback = null;
+
+        callback.sendSuccess(serviceType);
     }
 
     @Override
     public synchronized void onStartDiscoveryFailed(final String serviceType, final int errorCode) {
         Log.e(LOGTAG, "onStartDiscoveryFailed: " + serviceType + "(" + errorCode + ")");
-        if (mStartCallback == null) {
-            return;
+
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStartCallback;
         }
-        mStartCallback.sendError(errorCode);
-        mStartCallback = null;
+
+        callback.sendError(errorCode);
     }
 
     @Override
     public synchronized void onDiscoveryStopped(final String serviceType) {
         Log.d(LOGTAG, "onDiscoveryStopped: " + serviceType);
-        if (mStopCallback == null) {
+
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStopCallback;
+        }
+
+        if (callback == null) {
             return;
         }
-        mStopCallback.sendSuccess(serviceType);
-        mStopCallback = null;
+
+        callback.sendSuccess(serviceType);
     }
 
     @Override
     public synchronized void onStopDiscoveryFailed(final String serviceType, final int errorCode) {
         Log.e(LOGTAG, "onStopDiscoveryFailed: " + serviceType + "(" + errorCode + ")");
-        if (mStopCallback == null) {
+
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStopCallback;
+        }
+
+        if (callback == null) {
             return;
         }
-        mStopCallback.sendError(errorCode);
-        mStopCallback = null;
+
+        callback.sendError(errorCode);
     }
 
     @Override
     public void onServiceFound(final NsdServiceInfo serviceInfo) {
         Log.d(LOGTAG, "onServiceFound: " + serviceInfo.getServiceName());
-        JSONObject json = null;
+        JSONObject json;
         try {
             json = NsdMulticastDNSManager.toJSON(serviceInfo);
         } catch (JSONException e) {
             throw new RuntimeException(e);
         }
         GeckoAppShell.sendRequestToGecko(new GeckoRequest("NsdManager:ServiceFound", json) {
             @Override
             public void onResponse(NativeJSObject nativeJSObject) {
                 // don't care return value.
             }
         });
     }
 
     @Override
     public void onServiceLost(final NsdServiceInfo serviceInfo) {
         Log.d(LOGTAG, "onServiceLost: " + serviceInfo.getServiceName());
-        JSONObject json = null;
+        JSONObject json;
         try {
             json = NsdMulticastDNSManager.toJSON(serviceInfo);
         } catch (JSONException e) {
             throw new RuntimeException(e);
         }
         GeckoAppShell.sendRequestToGecko(new GeckoRequest("NsdManager:ServiceLost", json) {
             @Override
             public void onResponse(NativeJSObject nativeJSObject) {
@@ -356,28 +383,26 @@ class RegistrationListener implements Ns
     private EventCallback mStopCallback = null;
 
     RegistrationListener(final NsdManager nsdManager) {
         this.nsdManager = nsdManager;
     }
 
     public void registerService(final int port, final String serviceName, final String serviceType, final Map<String, String> attributes, final EventCallback callback) {
         Log.d(LOGTAG, "registerService: " + serviceName + "." + serviceType + ":" + port);
-        synchronized (this) {
-            if (mStartCallback != null) {
-                throw new RuntimeException("Previous operation is not finished");
-            }
-            mStartCallback = callback;
-        }
 
         NsdServiceInfo serviceInfo = new NsdServiceInfo();
         serviceInfo.setPort(port);
         serviceInfo.setServiceName(serviceName);
         serviceInfo.setServiceType(serviceType);
         setAttributes(serviceInfo, attributes);
+
+        synchronized (this) {
+            mStartCallback = callback;
+        }
         nsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, this);
     }
 
     @TargetApi(21)
     private void setAttributes(final NsdServiceInfo serviceInfo, final Map<String, String> attributes) {
         if (attributes == null || !Versions.feature21Plus) {
             return;
         }
@@ -385,120 +410,130 @@ class RegistrationListener implements Ns
         for (Map.Entry<String, String> entry : attributes.entrySet()) {
             serviceInfo.setAttribute(entry.getKey(), entry.getValue());
         }
     }
 
     public void unregisterService(final EventCallback callback) {
         Log.d(LOGTAG, "unregisterService");
         synchronized (this) {
-            if (mStopCallback != null) {
-                throw new RuntimeException("Previous operation is not finished");
-            }
             mStopCallback = callback;
         }
+
         nsdManager.unregisterService(this);
     }
 
     @Override
     public synchronized void onServiceRegistered(final NsdServiceInfo serviceInfo) {
         Log.d(LOGTAG, "onServiceRegistered: " + serviceInfo.getServiceName());
 
-        if (mStartCallback == null) {
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStartCallback;
+        }
+
+        if (callback == null) {
             return;
         }
 
         try {
-            mStartCallback.sendSuccess(NsdMulticastDNSManager.toJSON(serviceInfo));
+            callback.sendSuccess(NsdMulticastDNSManager.toJSON(serviceInfo));
         } catch (JSONException e) {
             throw new RuntimeException(e);
         }
-        mStartCallback = null;
     }
 
     @Override
     public synchronized void onRegistrationFailed(final NsdServiceInfo serviceInfo, final int errorCode) {
         Log.e(LOGTAG, "onRegistrationFailed: " + serviceInfo.getServiceName() + "(" + errorCode + ")");
-        if (mStartCallback == null) {
-            return;
+
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStartCallback;
         }
-        mStartCallback.sendError(errorCode);
-        mStartCallback = null;
+
+        callback.sendError(errorCode);
     }
 
     @Override
     public synchronized void onServiceUnregistered(final NsdServiceInfo serviceInfo) {
         Log.d(LOGTAG, "onServiceUnregistered: " + serviceInfo.getServiceName());
-        if (mStopCallback == null) {
+
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStopCallback;
+        }
+
+        if (callback == null) {
             return;
         }
+
         try {
-            mStopCallback.sendSuccess(NsdMulticastDNSManager.toJSON(serviceInfo));
+            callback.sendSuccess(NsdMulticastDNSManager.toJSON(serviceInfo));
         } catch (JSONException e) {
             throw new RuntimeException(e);
-
         }
-        mStopCallback = null;
     }
 
     @Override
     public synchronized void onUnregistrationFailed(final NsdServiceInfo serviceInfo, final int errorCode) {
         Log.e(LOGTAG, "onUnregistrationFailed: " + serviceInfo.getServiceName() + "(" + errorCode + ")");
-        if (mStopCallback == null) {
+
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStopCallback;
+        }
+
+        if (callback == null) {
             return;
         }
-        mStopCallback.sendError(errorCode);
-        mStopCallback = null;
+
+        callback.sendError(errorCode);
     }
 }
 
 @TargetApi(16)
 class ResolveListener implements NsdManager.ResolveListener {
     private static final String LOGTAG = "GeckoMDNSManager";
     private final NsdManager nsdManager;
 
     // Callback is called from different thread, and the callback can be called only once.
     private EventCallback mCallback = null;
 
     public ResolveListener(final NsdManager nsdManager) {
         this.nsdManager = nsdManager;
     }
 
     public void resolveService(final String serviceName, final String serviceType, final EventCallback callback) {
-        synchronized (this) {
-            if (mCallback != null) {
-                throw new RuntimeException("Previous operation is not finished");
-            }
-            mCallback = callback;
-        }
-
         NsdServiceInfo serviceInfo = new NsdServiceInfo();
         serviceInfo.setServiceName(serviceName);
         serviceInfo.setServiceType(serviceType);
+
+        mCallback = callback;
         nsdManager.resolveService(serviceInfo, this);
     }
 
 
     @Override
     public synchronized void onResolveFailed(final NsdServiceInfo serviceInfo, final int errorCode) {
         Log.e(LOGTAG, "onResolveFailed: " + serviceInfo.getServiceName() + "(" + errorCode + ")");
+
         if (mCallback == null) {
             return;
         }
         mCallback.sendError(errorCode);
-        mCallback = null;
     }
 
     @Override
     public synchronized void onServiceResolved(final NsdServiceInfo serviceInfo) {
         Log.d(LOGTAG, "onServiceResolved: " + serviceInfo.getServiceName());
+
         if (mCallback == null) {
             return;
         }
 
         try {
             mCallback.sendSuccess(NsdMulticastDNSManager.toJSON(serviceInfo));
         } catch (JSONException e) {
             throw new RuntimeException(e);
         }
-        mCallback = null;
     }
 }
--- a/mobile/android/modules/MulticastDNS.jsm
+++ b/mobile/android/modules/MulticastDNS.jsm
@@ -78,16 +78,17 @@ ServiceManager.prototype = {
     log("removeListener: " + aServiceType + ", " + aListener);
 
     if (!this.listeners[aServiceType]) {
       log("listener doesn't exist");
       return;
     }
     let index = this.listeners[aServiceType].indexOf(aListener);
     if (index < 0) {
+      log("listener doesn't exist");
       return;
     }
 
     this.listeners[aServiceType].splice(index, 1);
     --this.numListeners;
 
     if (this.numListeners === 0) {
       this.unregisterEvent();
@@ -144,44 +145,54 @@ function parsePropertyBag2(bag) {
 function MulticastDNS() {
   this.serviceManager = new ServiceManager();
 }
 
 MulticastDNS.prototype = {
   startDiscovery: function(aServiceType, aListener) {
     this.serviceManager.addListener(aServiceType, aListener);
 
-    send("NsdManager:DiscoverServices", { serviceType: aServiceType }, (result, err) => {
+    let serviceInfo = {
+      serviceType: aServiceType,
+      uniqueId: aListener.uuid
+    };
+
+    send("NsdManager:DiscoverServices", serviceInfo, (result, err) => {
       if (err) {
         log("onStartDiscoveryFailed: " + aServiceType + " (" + err + ")");
         this.serviceManager.removeListener(aServiceType, aListener);
         aListener.onStartDiscoveryFailed(aServiceType, err);
       } else {
         aListener.onDiscoveryStarted(result);
       }
     });
   },
 
   stopDiscovery: function(aServiceType, aListener) {
     this.serviceManager.removeListener(aServiceType, aListener);
 
-    send("NsdManager:StopServiceDiscovery", {}, (result, err) => {
+    let serviceInfo = {
+      uniqueId: aListener.uuid
+    };
+
+    send("NsdManager:StopServiceDiscovery", serviceInfo, (result, err) => {
       if (err) {
         log("onStopDiscoveryFailed: " + aServiceType + " (" + err + ")");
         aListener.onStopDiscoveryFailed(aServiceType, err);
       } else {
         aListener.onDiscoveryStopped(aServiceType);
       }
     });
   },
 
   registerService: function(aServiceInfo, aListener) {
     let serviceInfo = {
       port: aServiceInfo.port,
       serviceType: aServiceInfo.serviceType,
+      uniqueId: aListener.uuid
     };
 
     try {
       serviceInfo.host = aServiceInfo.host;
     } catch(e) {
       // host unspecified
     }
     try {
@@ -201,17 +212,21 @@ MulticastDNS.prototype = {
         aListener.onRegistrationFailed(aServiceInfo, err);
       } else {
         aListener.onServiceRegistered(result);
       }
     });
   },
 
   unregisterService: function(aServiceInfo, aListener) {
-    send("NsdManager:UnregisterService", {}, (result, err) => {
+    let serviceInfo = {
+      uniqueId: aListener.uuid
+    };
+
+    send("NsdManager:UnregisterService", serviceInfo, (result, err) => {
       if (err) {
         log("onUnregistrationFailed: (" + err + ")");
         aListener.onUnregistrationFailed(aServiceInfo, err);
       } else {
         aListener.onServiceUnregistered(aServiceInfo);
       }
     });
   },