Backed out changeset f02de3ec24e3 (bug 1471408) for causing Android build bustages on VirtualPresentation.java. r=backout
authorCosmin Sabou <csabou@mozilla.com>
Thu, 28 Jun 2018 13:34:36 +0300
changeset 424158 6041c030780420b6205cf2d6640513606609884c
parent 424157 b429b9fb68f1a954c4a9f8dba8e845cf7f569a56
child 424173 6bcf0177fc94c7fc4da19057699de0b12a3c3d7a
child 424201 20381e4a2de73b5c6403a9ecc718fca48a8e38fd
push id34199
push usercsabou@mozilla.com
push dateThu, 28 Jun 2018 10:35:05 +0000
treeherdermozilla-central@6041c0307804 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1471408
milestone63.0a1
backs outf02de3ec24e389344449714c4b250e199edbae23
first release with
nightly linux32
6041c0307804 / 63.0a1 / 20180628130527 / files
nightly linux64
6041c0307804 / 63.0a1 / 20180628130527 / files
nightly mac
6041c0307804 / 63.0a1 / 20180628130527 / files
nightly win32
6041c0307804 / 63.0a1 / 20180628130527 / files
nightly win64
6041c0307804 / 63.0a1 / 20180628130527 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset f02de3ec24e3 (bug 1471408) for causing Android build bustages on VirtualPresentation.java. r=backout
mobile/android/base/java/org/mozilla/gecko/RemotePresentationService.java
mobile/android/base/java/org/mozilla/gecko/VirtualPresentation.java
mobile/android/base/java/org/mozilla/gecko/mdns/DiscoveryListener.java
mobile/android/base/java/org/mozilla/gecko/mdns/DummyMulticastDNSManager.java
mobile/android/base/java/org/mozilla/gecko/mdns/MulticastDNSEventManager.java
mobile/android/base/java/org/mozilla/gecko/mdns/MulticastDNSManager.java
mobile/android/base/java/org/mozilla/gecko/mdns/NsdMulticastDNSManager.java
mobile/android/base/java/org/mozilla/gecko/mdns/RegistrationListener.java
mobile/android/base/java/org/mozilla/gecko/mdns/ResolveListener.java
--- a/mobile/android/base/java/org/mozilla/gecko/RemotePresentationService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/RemotePresentationService.java
@@ -1,23 +1,45 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * vim: ts=4 sw=4 expandtab:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
+import org.mozilla.gecko.ScreenManagerHelper;
+import org.mozilla.geckoview.GeckoRuntime;
+import org.mozilla.geckoview.GeckoSession;
+import org.mozilla.geckoview.GeckoSessionSettings;
+import org.mozilla.geckoview.GeckoView;
+
+import com.google.android.gms.cast.CastMediaControlIntent;
 import com.google.android.gms.cast.CastPresentation;
 import com.google.android.gms.cast.CastRemoteDisplayLocalService;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.GooglePlayServicesUtil;
 
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.media.MediaControlIntent;
+import android.support.v7.media.MediaRouteSelector;
+import android.support.v7.media.MediaRouter.RouteInfo;
+import android.support.v7.media.MediaRouter;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.Display;
+import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
+import android.widget.RelativeLayout;
+
+import java.util.HashMap;
+import java.util.Map;
 
 /*
  * Service to keep the remote display running even when the app goes into the background
  */
 public class RemotePresentationService extends CastRemoteDisplayLocalService {
 
     private static final String LOGTAG = "RemotePresentationService";
     private CastPresentation presentation;
@@ -71,8 +93,55 @@ public class RemotePresentationService e
         try {
             presentation.show();
         } catch (WindowManager.InvalidDisplayException ex) {
             Log.e(LOGTAG, "Unable to show presentation, display was removed.", ex);
             dismissPresentation();
         }
     }
 }
+
+class VirtualPresentation extends CastPresentation {
+    private static final String LOGTAG = "VirtualPresentation";
+    private static final String PRESENTATION_VIEW_URI = "chrome://browser/content/PresentationView.xul";
+
+    private RelativeLayout layout;
+    private GeckoView view;
+    private String deviceId;
+    private int screenId;
+
+    public VirtualPresentation(Context context, Display display) {
+        super(context, display);
+    }
+
+    public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
+    public void setScreenId(int screenId) { this.screenId = screenId; }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        /*
+         * NOTICE: The context get from getContext() is different to the context
+         * of the application. Presentaion has its own context to get correct
+         * resources.
+         */
+
+        final GeckoSession session = new GeckoSession();
+        session.getSettings().setString(GeckoSessionSettings.CHROME_URI,
+                                        PRESENTATION_VIEW_URI + "#" + deviceId);
+        session.getSettings().setInt(GeckoSessionSettings.SCREEN_ID, screenId);
+
+        // Create new GeckoView
+        view = new GeckoView(getContext());
+        view.setSession(session, GeckoApplication.ensureRuntime(getContext()));
+        view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+                                              LayoutParams.MATCH_PARENT));
+
+        // Create new layout to put the GeckoView
+        layout = new RelativeLayout(getContext());
+        layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+                                                LayoutParams.MATCH_PARENT));
+        layout.addView(view);
+
+        setContentView(layout);
+    }
+}
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/VirtualPresentation.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
- * vim: ts=4 sw=4 expandtab:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko;
-
-import org.mozilla.geckoview.GeckoSession;
-import org.mozilla.geckoview.GeckoSessionSettings;
-import org.mozilla.geckoview.GeckoView;
-
-import com.google.android.gms.cast.CastPresentation;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.view.Display;
-import android.view.ViewGroup.LayoutParams;
-import android.widget.RelativeLayout;
-
-
-class VirtualPresentation extends CastPresentation {
-    private static final String LOGTAG = "VirtualPresentation";
-    private static final String PRESENTATION_VIEW_URI = "chrome://browser/content/PresentationView.xul";
-
-    private RelativeLayout layout;
-    private GeckoView view;
-    private String deviceId;
-    private int screenId;
-
-    public VirtualPresentation(Context context, Display display) {
-        super(context, display);
-    }
-
-    public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
-    public void setScreenId(int screenId) { this.screenId = screenId; }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        /*
-         * NOTICE: The context get from getContext() is different to the context
-         * of the application. Presentaion has its own context to get correct
-         * resources.
-         */
-
-        final GeckoSession session = new GeckoSession();
-        session.getSettings().setString(GeckoSessionSettings.CHROME_URI,
-                PRESENTATION_VIEW_URI + "#" + deviceId);
-        session.getSettings().setInt(GeckoSessionSettings.SCREEN_ID, screenId);
-
-        // Create new GeckoView
-        view = new GeckoView(getContext());
-        view.setSession(session, GeckoApplication.ensureRuntime(getContext()));
-        view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
-                LayoutParams.MATCH_PARENT));
-
-        // Create new layout to put the GeckoView
-        layout = new RelativeLayout(getContext());
-        layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
-                LayoutParams.MATCH_PARENT));
-        layout.addView(view);
-
-        setContentView(layout);
-    }
-}
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/mdns/DiscoveryListener.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.mdns;
-
-import org.mozilla.gecko.EventDispatcher;
-import org.mozilla.gecko.util.EventCallback;
-
-import android.annotation.TargetApi;
-import android.net.nsd.NsdManager;
-import android.net.nsd.NsdServiceInfo;
-import android.util.Log;
-
-@TargetApi(16)
-class DiscoveryListener implements NsdManager.DiscoveryListener {
-    private static final boolean DEBUG = false;
-    private static final String LOGTAG = "GeckoMDNSManager";
-    private final NsdManager nsdManager;
-
-    // Callbacks are called from different thread, and every callback can be called only once.
-    private EventCallback mStartCallback = null;
-    private EventCallback mStopCallback = null;
-
-    DiscoveryListener(final NsdManager nsdManager) {
-        this.nsdManager = nsdManager;
-    }
-
-    public void discoverServices(final String serviceType, final EventCallback callback) {
-        synchronized (this) {
-            mStartCallback = callback;
-        }
-        nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, this);
-    }
-
-    public void stopServiceDiscovery(final EventCallback callback) {
-        synchronized (this) {
-            mStopCallback = callback;
-        }
-        nsdManager.stopServiceDiscovery(this);
-    }
-
-    @Override
-    public synchronized void onDiscoveryStarted(final String serviceType) {
-        if (DEBUG) {
-            Log.d(LOGTAG, "onDiscoveryStarted: " + serviceType);
-        }
-
-        EventCallback callback;
-        synchronized (this) {
-            callback = mStartCallback;
-        }
-
-        if (callback == null) {
-            return;
-        }
-
-        callback.sendSuccess(serviceType);
-    }
-
-    @Override
-    public synchronized void onStartDiscoveryFailed(final String serviceType, final int errorCode) {
-        Log.e(LOGTAG, "onStartDiscoveryFailed: " + serviceType + "(" + errorCode + ")");
-
-        EventCallback callback;
-        synchronized (this) {
-            callback = mStartCallback;
-        }
-
-        callback.sendError(errorCode);
-    }
-
-    @Override
-    public synchronized void onDiscoveryStopped(final String serviceType) {
-        if (DEBUG) {
-            Log.d(LOGTAG, "onDiscoveryStopped: " + serviceType);
-        }
-
-        EventCallback callback;
-        synchronized (this) {
-            callback = mStopCallback;
-        }
-
-        if (callback == null) {
-            return;
-        }
-
-        callback.sendSuccess(serviceType);
-    }
-
-    @Override
-    public synchronized void onStopDiscoveryFailed(final String serviceType, final int errorCode) {
-        Log.e(LOGTAG, "onStopDiscoveryFailed: " + serviceType + "(" + errorCode + ")");
-
-        EventCallback callback;
-        synchronized (this) {
-            callback = mStopCallback;
-        }
-
-        if (callback == null) {
-            return;
-        }
-
-        callback.sendError(errorCode);
-    }
-
-    @Override
-    public void onServiceFound(final NsdServiceInfo serviceInfo) {
-        if (DEBUG) {
-            Log.d(LOGTAG, "onServiceFound: " + serviceInfo.getServiceName());
-        }
-
-        EventDispatcher.getInstance().dispatch(
-                "NsdManager:ServiceFound", NsdMulticastDNSManager.toBundle(serviceInfo));
-    }
-
-    @Override
-    public void onServiceLost(final NsdServiceInfo serviceInfo) {
-        if (DEBUG) {
-            Log.d(LOGTAG, "onServiceLost: " + serviceInfo.getServiceName());
-        }
-
-        EventDispatcher.getInstance().dispatch(
-                "NdManager:ServiceLost", NsdMulticastDNSManager.toBundle(serviceInfo));
-    }
-}
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/mdns/DummyMulticastDNSManager.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.mdns;
-
-import org.mozilla.gecko.util.EventCallback;
-import org.mozilla.gecko.util.GeckoBundle;
-import org.mozilla.gecko.util.BundleEventListener;
-
-import android.util.Log;
-
-class DummyMulticastDNSManager extends MulticastDNSManager implements BundleEventListener {
-    static final int FAILURE_UNSUPPORTED = -65544;
-    private final MulticastDNSEventManager mEventManager;
-
-    public DummyMulticastDNSManager() {
-        mEventManager = new MulticastDNSEventManager(this);
-    }
-
-    @Override
-    public void init() {
-        mEventManager.init();
-    }
-
-    @Override
-    public void tearDown() {
-        mEventManager.tearDown();
-    }
-
-    @Override // BundleEventListener
-    public void handleMessage(final String event, final GeckoBundle message,
-                              final EventCallback callback) {
-        if (DEBUG) {
-            Log.v(LOGTAG, "handleMessage: " + event);
-        }
-        callback.sendError(FAILURE_UNSUPPORTED);
-    }
-}
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/mdns/MulticastDNSEventManager.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.mdns;
-
-import org.mozilla.gecko.EventDispatcher;
-import org.mozilla.gecko.util.BundleEventListener;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import android.support.annotation.UiThread;
-
-/**
- * Mix-in class for MulticastDNSManagers to call EventDispatcher.
- */
-class MulticastDNSEventManager {
-    private BundleEventListener mListener = null;
-    private boolean mEventsRegistered = false;
-
-    MulticastDNSEventManager(final BundleEventListener listener) {
-        mListener = listener;
-    }
-
-    @UiThread
-    public void init() {
-        ThreadUtils.assertOnUiThread();
-
-        if (mEventsRegistered || mListener == null) {
-            return;
-        }
-
-        registerEvents();
-        mEventsRegistered = true;
-    }
-
-    @UiThread
-    public void tearDown() {
-        ThreadUtils.assertOnUiThread();
-
-        if (!mEventsRegistered || mListener == null) {
-            return;
-        }
-
-        unregisterEvents();
-        mEventsRegistered = false;
-    }
-
-    private void registerEvents() {
-        EventDispatcher.getInstance().registerGeckoThreadListener(mListener,
-                "NsdManager:DiscoverServices",
-                "NsdManager:StopServiceDiscovery",
-                "NsdManager:RegisterService",
-                "NsdManager:UnregisterService",
-                "NsdManager:ResolveService");
-    }
-
-    private void unregisterEvents() {
-        EventDispatcher.getInstance().unregisterGeckoThreadListener(mListener,
-                "NsdManager:DiscoverServices",
-                "NsdManager:StopServiceDiscovery",
-                "NsdManager:RegisterService",
-                "NsdManager:UnregisterService",
-                "NsdManager:ResolveService");
-    }
-}
\ No newline at end of file
--- a/mobile/android/base/java/org/mozilla/gecko/mdns/MulticastDNSManager.java
+++ b/mobile/android/base/java/org/mozilla/gecko/mdns/MulticastDNSManager.java
@@ -1,16 +1,33 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.mdns;
 
+import org.mozilla.gecko.AppConstants.Versions;
+import org.mozilla.gecko.EventDispatcher;
+import org.mozilla.gecko.util.EventCallback;
+import org.mozilla.gecko.util.GeckoBundle;
+import org.mozilla.gecko.util.BundleEventListener;
+import org.mozilla.gecko.util.ThreadUtils;
+
+import android.annotation.TargetApi;
 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 boolean DEBUG = false;
@@ -22,8 +39,491 @@ public abstract class MulticastDNSManage
             instance = new DummyMulticastDNSManager();
         }
         return instance;
     }
 
     public abstract void init();
     public abstract void tearDown();
 }
+
+/**
+ * Mix-in class for MulticastDNSManagers to call EventDispatcher.
+ */
+class MulticastDNSEventManager {
+    private BundleEventListener mListener = null;
+    private boolean mEventsRegistered = false;
+
+    MulticastDNSEventManager(final BundleEventListener listener) {
+        mListener = listener;
+    }
+
+    @UiThread
+    public void init() {
+        ThreadUtils.assertOnUiThread();
+
+        if (mEventsRegistered || mListener == null) {
+            return;
+        }
+
+        registerEvents();
+        mEventsRegistered = true;
+    }
+
+    @UiThread
+    public void tearDown() {
+        ThreadUtils.assertOnUiThread();
+
+        if (!mEventsRegistered || mListener == null) {
+            return;
+        }
+
+        unregisterEvents();
+        mEventsRegistered = false;
+    }
+
+    private void registerEvents() {
+        EventDispatcher.getInstance().registerGeckoThreadListener(mListener,
+                "NsdManager:DiscoverServices",
+                "NsdManager:StopServiceDiscovery",
+                "NsdManager:RegisterService",
+                "NsdManager:UnregisterService",
+                "NsdManager:ResolveService");
+    }
+
+    private void unregisterEvents() {
+        EventDispatcher.getInstance().unregisterGeckoThreadListener(mListener,
+                "NsdManager:DiscoverServices",
+                "NsdManager:StopServiceDiscovery",
+                "NsdManager:RegisterService",
+                "NsdManager:UnregisterService",
+                "NsdManager:ResolveService");
+    }
+}
+
+class NsdMulticastDNSManager extends MulticastDNSManager implements BundleEventListener {
+    private final NsdManager nsdManager;
+    private final MulticastDNSEventManager mEventManager;
+    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 // BundleEventListener
+    public void handleMessage(final String event, final GeckoBundle message,
+                              final EventCallback callback) {
+        if (DEBUG) {
+            Log.v(LOGTAG, "handleMessage: " + event);
+        }
+
+        switch (event) {
+            case "NsdManager:DiscoverServices": {
+                DiscoveryListener listener = new DiscoveryListener(nsdManager);
+                listener.discoverServices(message.getString("serviceType"), callback);
+                mDiscoveryListeners.put(message.getString("uniqueId"), listener);
+                break;
+            }
+            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": {
+                RegistrationListener listener = new RegistrationListener(nsdManager);
+                listener.registerService(message.getInt("port"),
+                        message.getString("serviceName", android.os.Build.MODEL),
+                        message.getString("serviceType"),
+                        parseAttributes(message.getBundleArray("attributes")),
+                        callback);
+                mRegistrationListeners.put(message.getString("uniqueId"), listener);
+                break;
+            }
+            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": {
+                (new ResolveListener(nsdManager)).resolveService(message.getString("serviceName"),
+                        message.getString("serviceType"),
+                        callback);
+                break;
+            }
+        }
+    }
+
+    private Map<String, String> parseAttributes(final GeckoBundle[] jsobjs) {
+        if (jsobjs == null || jsobjs.length == 0 || !Versions.feature21Plus) {
+            return null;
+        }
+
+        final Map<String, String> attributes = new HashMap<>(jsobjs.length);
+        for (final GeckoBundle obj : jsobjs) {
+            attributes.put(obj.getString("name"), obj.getString("value"));
+        }
+
+        return attributes;
+    }
+
+    @TargetApi(16)
+    public static GeckoBundle toBundle(final NsdServiceInfo serviceInfo) {
+        final GeckoBundle obj = new GeckoBundle();
+
+        final InetAddress host = serviceInfo.getHost();
+        if (host != null) {
+            obj.putString("host", host.getCanonicalHostName());
+            obj.putString("address", host.getHostAddress());
+        }
+
+        int port = serviceInfo.getPort();
+        if (port != 0) {
+            obj.putInt("port", port);
+        }
+
+        final String serviceName = serviceInfo.getServiceName();
+        if (serviceName != null) {
+            obj.putString("serviceName", serviceName);
+        }
+
+        final String serviceType = serviceInfo.getServiceType();
+        if (serviceType != null) {
+            obj.putString("serviceType", serviceType);
+        }
+
+        return obj;
+    }
+}
+
+class DummyMulticastDNSManager extends MulticastDNSManager implements BundleEventListener {
+    static final int FAILURE_UNSUPPORTED = -65544;
+    private final MulticastDNSEventManager mEventManager;
+
+    public DummyMulticastDNSManager() {
+        mEventManager = new MulticastDNSEventManager(this);
+    }
+
+    @Override
+    public void init() {
+        mEventManager.init();
+    }
+
+    @Override
+    public void tearDown() {
+        mEventManager.tearDown();
+    }
+
+    @Override // BundleEventListener
+    public void handleMessage(final String event, final GeckoBundle message,
+                              final EventCallback callback) {
+        if (DEBUG) {
+            Log.v(LOGTAG, "handleMessage: " + event);
+        }
+        callback.sendError(FAILURE_UNSUPPORTED);
+    }
+}
+
+@TargetApi(16)
+class DiscoveryListener implements NsdManager.DiscoveryListener {
+    private static final boolean DEBUG = false;
+    private static final String LOGTAG = "GeckoMDNSManager";
+    private final NsdManager nsdManager;
+
+    // Callbacks are called from different thread, and every callback can be called only once.
+    private EventCallback mStartCallback = null;
+    private EventCallback mStopCallback = null;
+
+    DiscoveryListener(final NsdManager nsdManager) {
+        this.nsdManager = nsdManager;
+    }
+
+    public void discoverServices(final String serviceType, final EventCallback callback) {
+        synchronized (this) {
+            mStartCallback = callback;
+        }
+        nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, this);
+    }
+
+    public void stopServiceDiscovery(final EventCallback callback) {
+        synchronized (this) {
+            mStopCallback = callback;
+        }
+        nsdManager.stopServiceDiscovery(this);
+    }
+
+    @Override
+    public synchronized void onDiscoveryStarted(final String serviceType) {
+        if (DEBUG) {
+            Log.d(LOGTAG, "onDiscoveryStarted: " + serviceType);
+        }
+
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStartCallback;
+        }
+
+        if (callback == null) {
+            return;
+        }
+
+        callback.sendSuccess(serviceType);
+    }
+
+    @Override
+    public synchronized void onStartDiscoveryFailed(final String serviceType, final int errorCode) {
+        Log.e(LOGTAG, "onStartDiscoveryFailed: " + serviceType + "(" + errorCode + ")");
+
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStartCallback;
+        }
+
+        callback.sendError(errorCode);
+    }
+
+    @Override
+    public synchronized void onDiscoveryStopped(final String serviceType) {
+        if (DEBUG) {
+            Log.d(LOGTAG, "onDiscoveryStopped: " + serviceType);
+        }
+
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStopCallback;
+        }
+
+        if (callback == null) {
+            return;
+        }
+
+        callback.sendSuccess(serviceType);
+    }
+
+    @Override
+    public synchronized void onStopDiscoveryFailed(final String serviceType, final int errorCode) {
+        Log.e(LOGTAG, "onStopDiscoveryFailed: " + serviceType + "(" + errorCode + ")");
+
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStopCallback;
+        }
+
+        if (callback == null) {
+            return;
+        }
+
+        callback.sendError(errorCode);
+    }
+
+    @Override
+    public void onServiceFound(final NsdServiceInfo serviceInfo) {
+        if (DEBUG) {
+            Log.d(LOGTAG, "onServiceFound: " + serviceInfo.getServiceName());
+        }
+
+        EventDispatcher.getInstance().dispatch(
+                "NsdManager:ServiceFound", NsdMulticastDNSManager.toBundle(serviceInfo));
+    }
+
+    @Override
+    public void onServiceLost(final NsdServiceInfo serviceInfo) {
+        if (DEBUG) {
+            Log.d(LOGTAG, "onServiceLost: " + serviceInfo.getServiceName());
+        }
+
+        EventDispatcher.getInstance().dispatch(
+                "NdManager:ServiceLost", NsdMulticastDNSManager.toBundle(serviceInfo));
+    }
+}
+
+@TargetApi(16)
+class RegistrationListener implements NsdManager.RegistrationListener {
+    private static final boolean DEBUG = false;
+    private static final String LOGTAG = "GeckoMDNSManager";
+    private final NsdManager nsdManager;
+
+    // Callbacks are called from different thread, and every callback can be called only once.
+    private EventCallback mStartCallback = null;
+    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) {
+        if (DEBUG) {
+            Log.d(LOGTAG, "registerService: " + serviceName + "." + serviceType + ":" + port);
+        }
+
+        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;
+        }
+
+        for (Map.Entry<String, String> entry : attributes.entrySet()) {
+            serviceInfo.setAttribute(entry.getKey(), entry.getValue());
+        }
+    }
+
+    public void unregisterService(final EventCallback callback) {
+        if (DEBUG) {
+            Log.d(LOGTAG, "unregisterService");
+        }
+        synchronized (this) {
+            mStopCallback = callback;
+        }
+
+        nsdManager.unregisterService(this);
+    }
+
+    @Override
+    public synchronized void onServiceRegistered(final NsdServiceInfo serviceInfo) {
+        if (DEBUG) {
+            Log.d(LOGTAG, "onServiceRegistered: " + serviceInfo.getServiceName());
+        }
+
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStartCallback;
+        }
+
+        if (callback == null) {
+            return;
+        }
+
+        callback.sendSuccess(NsdMulticastDNSManager.toBundle(serviceInfo));
+    }
+
+    @Override
+    public synchronized void onRegistrationFailed(final NsdServiceInfo serviceInfo, final int errorCode) {
+        Log.e(LOGTAG, "onRegistrationFailed: " + serviceInfo.getServiceName() + "(" + errorCode + ")");
+
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStartCallback;
+        }
+
+        callback.sendError(errorCode);
+    }
+
+    @Override
+    public synchronized void onServiceUnregistered(final NsdServiceInfo serviceInfo) {
+        if (DEBUG) {
+            Log.d(LOGTAG, "onServiceUnregistered: " + serviceInfo.getServiceName());
+        }
+
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStopCallback;
+        }
+
+        if (callback == null) {
+            return;
+        }
+
+        callback.sendSuccess(NsdMulticastDNSManager.toBundle(serviceInfo));
+    }
+
+    @Override
+    public synchronized void onUnregistrationFailed(final NsdServiceInfo serviceInfo, final int errorCode) {
+        Log.e(LOGTAG, "onUnregistrationFailed: " + serviceInfo.getServiceName() + "(" + errorCode + ")");
+
+        EventCallback callback;
+        synchronized (this) {
+            callback = mStopCallback;
+        }
+
+        if (callback == null) {
+            return;
+        }
+
+        callback.sendError(errorCode);
+    }
+}
+
+@TargetApi(16)
+class ResolveListener implements NsdManager.ResolveListener {
+    private static final boolean DEBUG = false;
+    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) {
+        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);
+    }
+
+    @Override
+    public synchronized void onServiceResolved(final NsdServiceInfo serviceInfo) {
+        if (DEBUG) {
+            Log.d(LOGTAG, "onServiceResolved: " + serviceInfo.getServiceName());
+        }
+
+        if (mCallback == null) {
+            return;
+        }
+
+        mCallback.sendSuccess(NsdMulticastDNSManager.toBundle(serviceInfo));
+    }
+}
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/mdns/NsdMulticastDNSManager.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.mdns;
-
-import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.util.EventCallback;
-import org.mozilla.gecko.util.GeckoBundle;
-import org.mozilla.gecko.util.BundleEventListener;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.net.nsd.NsdManager;
-import android.net.nsd.NsdServiceInfo;
-import android.util.Log;
-
-import java.net.InetAddress;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-class NsdMulticastDNSManager extends MulticastDNSManager implements BundleEventListener {
-    private final NsdManager nsdManager;
-    private final MulticastDNSEventManager mEventManager;
-    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 // BundleEventListener
-    public void handleMessage(final String event, final GeckoBundle message,
-                              final EventCallback callback) {
-        if (DEBUG) {
-            Log.v(LOGTAG, "handleMessage: " + event);
-        }
-
-        switch (event) {
-            case "NsdManager:DiscoverServices": {
-                DiscoveryListener listener = new DiscoveryListener(nsdManager);
-                listener.discoverServices(message.getString("serviceType"), callback);
-                mDiscoveryListeners.put(message.getString("uniqueId"), listener);
-                break;
-            }
-            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": {
-                RegistrationListener listener = new RegistrationListener(nsdManager);
-                listener.registerService(message.getInt("port"),
-                        message.getString("serviceName", android.os.Build.MODEL),
-                        message.getString("serviceType"),
-                        parseAttributes(message.getBundleArray("attributes")),
-                        callback);
-                mRegistrationListeners.put(message.getString("uniqueId"), listener);
-                break;
-            }
-            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": {
-                (new ResolveListener(nsdManager)).resolveService(message.getString("serviceName"),
-                        message.getString("serviceType"),
-                        callback);
-                break;
-            }
-        }
-    }
-
-    private Map<String, String> parseAttributes(final GeckoBundle[] jsobjs) {
-        if (jsobjs == null || jsobjs.length == 0 || !Versions.feature21Plus) {
-            return null;
-        }
-
-        final Map<String, String> attributes = new HashMap<>(jsobjs.length);
-        for (final GeckoBundle obj : jsobjs) {
-            attributes.put(obj.getString("name"), obj.getString("value"));
-        }
-
-        return attributes;
-    }
-
-    @TargetApi(16)
-    public static GeckoBundle toBundle(final NsdServiceInfo serviceInfo) {
-        final GeckoBundle obj = new GeckoBundle();
-
-        final InetAddress host = serviceInfo.getHost();
-        if (host != null) {
-            obj.putString("host", host.getCanonicalHostName());
-            obj.putString("address", host.getHostAddress());
-        }
-
-        int port = serviceInfo.getPort();
-        if (port != 0) {
-            obj.putInt("port", port);
-        }
-
-        final String serviceName = serviceInfo.getServiceName();
-        if (serviceName != null) {
-            obj.putString("serviceName", serviceName);
-        }
-
-        final String serviceType = serviceInfo.getServiceType();
-        if (serviceType != null) {
-            obj.putString("serviceType", serviceType);
-        }
-
-        return obj;
-    }
-}
-
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/mdns/RegistrationListener.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.mdns;
-
-import org.mozilla.gecko.AppConstants.Versions;
-
-import org.mozilla.gecko.util.EventCallback;
-
-import android.annotation.TargetApi;
-import android.net.nsd.NsdManager;
-import android.net.nsd.NsdServiceInfo;
-import android.util.Log;
-
-import java.util.Map;
-
-@TargetApi(16)
-class RegistrationListener implements NsdManager.RegistrationListener {
-    private static final boolean DEBUG = false;
-    private static final String LOGTAG = "GeckoMDNSManager";
-    private final NsdManager nsdManager;
-
-    // Callbacks are called from different thread, and every callback can be called only once.
-    private EventCallback mStartCallback = null;
-    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) {
-        if (DEBUG) {
-            Log.d(LOGTAG, "registerService: " + serviceName + "." + serviceType + ":" + port);
-        }
-
-        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;
-        }
-
-        for (Map.Entry<String, String> entry : attributes.entrySet()) {
-            serviceInfo.setAttribute(entry.getKey(), entry.getValue());
-        }
-    }
-
-    public void unregisterService(final EventCallback callback) {
-        if (DEBUG) {
-            Log.d(LOGTAG, "unregisterService");
-        }
-        synchronized (this) {
-            mStopCallback = callback;
-        }
-
-        nsdManager.unregisterService(this);
-    }
-
-    @Override
-    public synchronized void onServiceRegistered(final NsdServiceInfo serviceInfo) {
-        if (DEBUG) {
-            Log.d(LOGTAG, "onServiceRegistered: " + serviceInfo.getServiceName());
-        }
-
-        EventCallback callback;
-        synchronized (this) {
-            callback = mStartCallback;
-        }
-
-        if (callback == null) {
-            return;
-        }
-
-        callback.sendSuccess(NsdMulticastDNSManager.toBundle(serviceInfo));
-    }
-
-    @Override
-    public synchronized void onRegistrationFailed(final NsdServiceInfo serviceInfo, final int errorCode) {
-        Log.e(LOGTAG, "onRegistrationFailed: " + serviceInfo.getServiceName() + "(" + errorCode + ")");
-
-        EventCallback callback;
-        synchronized (this) {
-            callback = mStartCallback;
-        }
-
-        callback.sendError(errorCode);
-    }
-
-    @Override
-    public synchronized void onServiceUnregistered(final NsdServiceInfo serviceInfo) {
-        if (DEBUG) {
-            Log.d(LOGTAG, "onServiceUnregistered: " + serviceInfo.getServiceName());
-        }
-
-        EventCallback callback;
-        synchronized (this) {
-            callback = mStopCallback;
-        }
-
-        if (callback == null) {
-            return;
-        }
-
-        callback.sendSuccess(NsdMulticastDNSManager.toBundle(serviceInfo));
-    }
-
-    @Override
-    public synchronized void onUnregistrationFailed(final NsdServiceInfo serviceInfo, final int errorCode) {
-        Log.e(LOGTAG, "onUnregistrationFailed: " + serviceInfo.getServiceName() + "(" + errorCode + ")");
-
-        EventCallback callback;
-        synchronized (this) {
-            callback = mStopCallback;
-        }
-
-        if (callback == null) {
-            return;
-        }
-
-        callback.sendError(errorCode);
-    }
-}
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/mdns/ResolveListener.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.mdns;
-
-import org.mozilla.gecko.util.EventCallback;
-
-import android.annotation.TargetApi;
-import android.net.nsd.NsdManager;
-import android.net.nsd.NsdServiceInfo;
-import android.util.Log;
-
-@TargetApi(16)
-class ResolveListener implements NsdManager.ResolveListener {
-    private static final boolean DEBUG = false;
-    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) {
-        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);
-    }
-
-    @Override
-    public synchronized void onServiceResolved(final NsdServiceInfo serviceInfo) {
-        if (DEBUG) {
-            Log.d(LOGTAG, "onServiceResolved: " + serviceInfo.getServiceName());
-        }
-
-        if (mCallback == null) {
-            return;
-        }
-
-        mCallback.sendSuccess(NsdMulticastDNSManager.toBundle(serviceInfo));
-    }
-}