Bug 1361418 - Remove Java Addons from Firefox for Android. r=sebastian draft
authorNick Alexander <nalexander@mozilla.com>
Tue, 02 May 2017 11:49:16 -0700
changeset 571485 346f88882774f072316714cf637a54d771d81a9a
parent 571414 bfc7b187005cabbc828ed9f5b61daf139c3cfd90
child 571572 f30409780a4611bbd93ff07a789847c6a07b59d1
child 571581 3052bec047e920bd0ba483d42343003a15671ea6
child 571973 477ef683f850ff11cfa128e17855666bb7758a7a
push id56812
push usernalexander@mozilla.com
push dateTue, 02 May 2017 18:49:51 +0000
reviewerssebastian
bugs1361418
milestone55.0a1
Bug 1361418 - Remove Java Addons from Firefox for Android. r=sebastian The Java Addons mechanism never got traction and is not Web Extensions compatible. Removing it simplifies the product and the build system. MozReview-Commit-ID: ABUxkqqMISa
config/recurse.mk
mobile/android/app/build.gradle
mobile/android/base/Makefile.in
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/javaaddons/JavaAddonManager.java
mobile/android/base/java/org/mozilla/gecko/javaaddons/JavaAddonManagerV1.java
mobile/android/base/moz.build
mobile/android/config/proguard/proguard.cfg
mobile/android/javaaddons/Makefile.in
mobile/android/javaaddons/java/org/mozilla/javaaddons/JavaAddonInterfaceV1.java
mobile/android/javaaddons/moz.build
mobile/android/modules/JavaAddonManager.jsm
mobile/android/modules/moz.build
mobile/android/moz.build
mobile/android/tests/browser/chrome/chrome.ini
mobile/android/tests/browser/chrome/test_java_addons.html
mobile/android/tests/browser/robocop/roboextender/Makefile.in
mobile/android/tests/javaaddons/AndroidManifest.xml.in
mobile/android/tests/javaaddons/Makefile.in
mobile/android/tests/javaaddons/moz.build
mobile/android/tests/javaaddons/res/values/strings.xml
mobile/android/tests/javaaddons/src/org/mozilla/javaaddons/test/ClassWithNoRecognizedConstructors.java
mobile/android/tests/javaaddons/src/org/mozilla/javaaddons/test/JavaAddonV0.java
mobile/android/tests/javaaddons/src/org/mozilla/javaaddons/test/JavaAddonV1.java
mobile/android/tests/moz.build
--- a/config/recurse.mk
+++ b/config/recurse.mk
@@ -166,20 +166,16 @@ js/xpconnect/src/export: dom/bindings/ex
 accessible/xpcom/export: xpcom/xpidl/export
 
 # The widget binding generator code is part of the annotationProcessors.
 widget/android/bindings/export: build/annotationProcessors/export
 
 # .xpt generation needs the xpidl lex/yacc files
 xpcom/xpidl/export: xpcom/idl-parser/xpidl/export
 
-# The roboextender addon includes a classes.dex containing a test Java addon.
-# The test addon must be built first.
-mobile/android/tests/browser/robocop/roboextender/tools: mobile/android/tests/javaaddons/tools
-
 ifdef ENABLE_CLANG_PLUGIN
 $(filter-out config/host build/unix/stdc++compat/% build/clang-plugin/%,$(compile_targets)): build/clang-plugin/target build/clang-plugin/tests/target
 build/clang-plugin/tests/target: build/clang-plugin/target
 endif
 
 # Interdependencies that moz.build world don't know about yet for compilation.
 # Note some others are hardcoded or "guessed" in recursivemake.py and emitter.py
 ifeq ($(MOZ_WIDGET_TOOLKIT),gtk3)
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -101,17 +101,16 @@ android {
 
             aidl {
                 srcDir "${topsrcdir}/mobile/android/base/aidl"
             }
 
             java {
                 srcDir "${topsrcdir}/mobile/android/base/java"
                 srcDir "${topsrcdir}/mobile/android/search/java"
-                srcDir "${topsrcdir}/mobile/android/javaaddons/java"
                 srcDir "${topsrcdir}/mobile/android/services/src/main/java"
 
                 if (mozconfig.substs.MOZ_ANDROID_MLS_STUMBLER) {
                     srcDir "${topsrcdir}/mobile/android/stumbler/java"
                 }
 
                 if (!mozconfig.substs.MOZ_CRASHREPORTER) {
                     exclude 'org/mozilla/gecko/CrashReporter.java'
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -141,17 +141,16 @@ GECKOVIEW_JARS += gecko-thirdparty-adjus
 endif
 
 geckoview_jars_classpath := $(subst $(NULL) ,:,$(strip $(GECKOVIEW_JARS)))
 
 FENNEC_JARS = \
   gecko-browser.jar \
   gecko-thirdparty.jar \
   services.jar \
-  ../javaaddons/javaaddons-1.0.jar \
   $(NULL)
 
 ifdef MOZ_WEBRTC
 FENNEC_JARS += webrtc.jar
 endif
 
 ifdef MOZ_ANDROID_SEARCH_ACTIVITY
 FENNEC_JARS += search-activity.jar
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -53,17 +53,16 @@ import org.mozilla.gecko.home.HomeConfig
 import org.mozilla.gecko.home.HomeConfigPrefsBackend;
 import org.mozilla.gecko.home.HomeFragment;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenInBackgroundListener;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.home.HomePanelsManager;
 import org.mozilla.gecko.home.HomeScreen;
 import org.mozilla.gecko.home.SearchEngine;
 import org.mozilla.gecko.icons.Icons;
-import org.mozilla.gecko.javaaddons.JavaAddonManager;
 import org.mozilla.gecko.media.VideoPlayer;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuItem;
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.notifications.NotificationHelper;
 import org.mozilla.gecko.overlays.ui.ShareDialog;
 import org.mozilla.gecko.permissions.Permissions;
@@ -802,17 +801,16 @@ public class BrowserApp extends GeckoApp
 
         mSearchEngineManager = new SearchEngineManager(this, distribution);
 
         // Init suggested sites engine in BrowserDB.
         final SuggestedSites suggestedSites = new SuggestedSites(appContext, distribution);
         final BrowserDB db = BrowserDB.from(profile);
         db.setSuggestedSites(suggestedSites);
 
-        JavaAddonManager.getInstance().init(appContext);
         mSharedPreferencesHelper = new SharedPreferencesHelper(appContext);
         mReadingListHelper = new ReadingListHelper(appContext, profile);
         mAccountsHelper = new AccountsHelper(appContext, profile);
 
         if (AppConstants.MOZ_ANDROID_BEAM) {
             NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this);
             if (nfc != null) {
                 nfc.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/javaaddons/JavaAddonManager.java
+++ /dev/null
@@ -1,170 +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.javaaddons;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-import dalvik.system.DexClassLoader;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.EventDispatcher;
-import org.mozilla.gecko.util.BundleEventListener;
-import org.mozilla.gecko.util.EventCallback;
-import org.mozilla.gecko.util.GeckoBundle;
-
-import java.io.File;
-import java.lang.reflect.Constructor;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * The manager for addon-provided Java code.
- *
- * Java code in addons can be loaded using the Dex:Load message, and unloaded
- * via the Dex:Unload message. Addon classes loaded are checked for a constructor
- * that takes a Map&lt;String, Handler.Callback&gt;. If such a constructor
- * exists, it is called and the objects populated into the map by the constructor
- * are registered as event listeners. If no such constructor exists, the default
- * constructor is invoked instead.
- *
- * Note: The Map and Handler.Callback classes were used in this API definition
- * rather than defining a custom class. This was done explicitly so that the
- * addon code can be compiled against the android.jar provided in the Android
- * SDK, rather than having to be compiled against Fennec source code.
- *
- * The Handler.Callback instances provided (as described above) are invoked with
- * Message objects when the corresponding events are dispatched. The Bundle
- * object attached to the Message will contain the "primitive" values from the
- * JSON of the event. ("primitive" includes bool/int/long/double/String). If
- * the addon callback wishes to synchronously return a value back to the event
- * dispatcher, they can do so by inserting the response string into the bundle
- * under the key "response".
- */
-public class JavaAddonManager implements BundleEventListener {
-    private static final String LOGTAG = "GeckoJavaAddonManager";
-
-    private static JavaAddonManager sInstance;
-
-    private final EventDispatcher mDispatcher;
-    private final Map<String, Map<String, BundleEventListener>> mAddonCallbacks;
-
-    private Context mApplicationContext;
-
-    public static JavaAddonManager getInstance() {
-        if (sInstance == null) {
-            sInstance = new JavaAddonManager();
-        }
-        return sInstance;
-    }
-
-    private JavaAddonManager() {
-        mDispatcher = EventDispatcher.getInstance();
-        mAddonCallbacks = new HashMap<>();
-    }
-
-    public void init(Context applicationContext) {
-        if (mApplicationContext != null) {
-            // we've already done this registration. don't do it again
-            return;
-        }
-        mApplicationContext = applicationContext;
-        mDispatcher.registerGeckoThreadListener(this,
-            "Dex:Load",
-            "Dex:Unload");
-        JavaAddonManagerV1.getInstance().init(applicationContext);
-    }
-
-    @Override // BundleEventListener
-    public void handleMessage(final String event, final GeckoBundle message,
-                              final EventCallback callback) {
-        if ("Dex:Load".equals(event)) {
-            final String zipFile = message.getString("zipfile");
-            final String implClass = message.getString("impl");
-            Log.d(LOGTAG, "Attempting to load classes.dex file from " + zipFile +
-                          " and instantiate " + implClass);
-            try {
-                final File tmpDir = mApplicationContext.getDir("dex", 0);
-                final DexClassLoader loader = new DexClassLoader(
-                        zipFile, tmpDir.getAbsolutePath(),
-                        null, mApplicationContext.getClassLoader());
-                final Class<?> c = loader.loadClass(implClass);
-                try {
-                    final Constructor<?> constructor = c.getDeclaredConstructor(Map.class);
-                    final Map<String, Handler.Callback> callbacks =
-                            new HashMap<String, Handler.Callback>();
-                    constructor.newInstance(callbacks);
-                    registerCallbacks(zipFile, callbacks);
-                } catch (final NoSuchMethodException nsme) {
-                    Log.d(LOGTAG, "Did not find constructor with parameters " +
-                                  "Map<String, Handler.Callback>. Falling back " +
-                                  "to default constructor...");
-                    // fallback for instances with no constructor that takes a Map<String,
-                    // Handler.Callback>
-                    c.newInstance();
-                }
-            } catch (final Exception e) {
-                Log.e(LOGTAG, "Unable to load dex successfully", e);
-            }
-
-        } else if ("Dex:Unload".equals(event)) {
-            final String zipFile = message.getString("zipfile");
-            unregisterCallbacks(zipFile);
-        }
-    }
-
-    private void registerCallbacks(String zipFile, Map<String, Handler.Callback> callbacks) {
-        Map<String, BundleEventListener> addonCallbacks = mAddonCallbacks.get(zipFile);
-        if (addonCallbacks != null) {
-            Log.w(LOGTAG, "Found pre-existing callbacks for zipfile [" + zipFile + "]; aborting re-registration!");
-            return;
-        }
-        addonCallbacks = new HashMap<>();
-        for (String event : callbacks.keySet()) {
-            CallbackWrapper wrapper = new CallbackWrapper(callbacks.get(event));
-            mDispatcher.registerGeckoThreadListener(wrapper, event);
-            addonCallbacks.put(event, wrapper);
-        }
-        mAddonCallbacks.put(zipFile, addonCallbacks);
-    }
-
-    private void unregisterCallbacks(String zipFile) {
-        Map<String, BundleEventListener> callbacks = mAddonCallbacks.remove(zipFile);
-        if (callbacks == null) {
-            Log.w(LOGTAG, "Attempting to unregister callbacks from zipfile [" + zipFile +
-                          "] which has no callbacks registered.");
-            return;
-        }
-        for (String event : callbacks.keySet()) {
-            mDispatcher.unregisterGeckoThreadListener(callbacks.get(event), event);
-        }
-    }
-
-    private static class CallbackWrapper implements BundleEventListener {
-        private final Handler.Callback mDelegate;
-
-        CallbackWrapper(Handler.Callback delegate) {
-            mDelegate = delegate;
-        }
-
-        @Override // BundleEventListener
-        public void handleMessage(final String event, final GeckoBundle message,
-                                  final EventCallback callback) {
-            final Message msg = new Message();
-            final Bundle data = message.toBundle();
-            data.putString("type", event);
-            msg.setData(data);
-            mDelegate.handleMessage(msg);
-
-            final GeckoBundle response = new GeckoBundle(1);
-            response.putString("response", data.getString("response"));
-            callback.sendSuccess(response);
-        }
-    }
-}
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/javaaddons/JavaAddonManagerV1.java
+++ /dev/null
@@ -1,308 +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.javaaddons;
-
-import android.content.Context;
-import android.util.Log;
-import android.util.Pair;
-import dalvik.system.DexClassLoader;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.EventDispatcher;
-import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.sync.Utils;
-import org.mozilla.gecko.util.BundleEventListener;
-import org.mozilla.gecko.util.EventCallback;
-import org.mozilla.gecko.util.GeckoBundle;
-import org.mozilla.gecko.util.GeckoJarReader;
-import org.mozilla.javaaddons.JavaAddonInterfaceV1;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.util.HashMap;
-import java.util.IdentityHashMap;
-import java.util.Map;
-
-public class JavaAddonManagerV1 implements BundleEventListener {
-    private static final String LOGTAG = "GeckoJavaAddonMgrV1";
-    public static final String MESSAGE_LOAD = "JavaAddonManagerV1:Load";
-    public static final String MESSAGE_UNLOAD = "JavaAddonManagerV1:Unload";
-
-    private static JavaAddonManagerV1 sInstance;
-
-    // Protected by static synchronized.
-    private Context mApplicationContext;
-
-    private final org.mozilla.gecko.EventDispatcher mDispatcher;
-
-    // Protected by synchronized (this).
-    private final Map<String, EventDispatcherImpl> mGUIDToDispatcherMap = new HashMap<>();
-
-    public static synchronized JavaAddonManagerV1 getInstance() {
-        if (sInstance == null) {
-            sInstance = new JavaAddonManagerV1();
-        }
-        return sInstance;
-    }
-
-    private JavaAddonManagerV1() {
-        mDispatcher = org.mozilla.gecko.EventDispatcher.getInstance();
-    }
-
-    public synchronized void init(Context applicationContext) {
-        if (mApplicationContext != null) {
-            // We've already registered; don't register again.
-            return;
-        }
-        mApplicationContext = applicationContext;
-        mDispatcher.registerGeckoThreadListener(this,
-                MESSAGE_LOAD,
-                MESSAGE_UNLOAD);
-    }
-
-    protected String getExtension(String filename) {
-        if (filename == null) {
-            return "";
-        }
-        final int last = filename.lastIndexOf(".");
-        if (last < 0) {
-            return "";
-        }
-        return filename.substring(last);
-    }
-
-    protected synchronized EventDispatcherImpl registerNewInstance(String classname, String filename)
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, IOException {
-        Log.d(LOGTAG, "Attempting to instantiate " + classname + " from filename " + filename);
-
-        // It's important to maintain the extension, either .dex, .apk, .jar.
-        final String extension = getExtension(filename);
-        final File dexFile = GeckoJarReader.extractStream(mApplicationContext, filename, mApplicationContext.getCacheDir(), "." + extension);
-        try {
-            if (dexFile == null) {
-                throw new IOException("Could not find file " + filename);
-            }
-            final File tmpDir = mApplicationContext.getDir("dex", 0); // We'd prefer getCodeCacheDir but it's API 21+.
-            final DexClassLoader loader = new DexClassLoader(dexFile.getAbsolutePath(), tmpDir.getAbsolutePath(), null, mApplicationContext.getClassLoader());
-            final Class<?> c = loader.loadClass(classname);
-            final Constructor<?> constructor = c.getDeclaredConstructor(Context.class, JavaAddonInterfaceV1.EventDispatcher.class);
-            final String guid = Utils.generateGuid();
-            final EventDispatcherImpl dispatcher = new EventDispatcherImpl(guid, filename);
-            final Object instance = constructor.newInstance(mApplicationContext, dispatcher);
-            mGUIDToDispatcherMap.put(guid, dispatcher);
-            return dispatcher;
-        } finally {
-            // DexClassLoader writes an optimized version, so we can get rid of our temporary extracted version.
-            if (dexFile != null) {
-                dexFile.delete();
-            }
-        }
-    }
-
-    @Override // BundleEventListener
-    public synchronized void handleMessage(final String event, final GeckoBundle message,
-                                           final EventCallback callback) {
-        switch (event) {
-            case MESSAGE_LOAD: {
-                if (callback == null) {
-                    throw new IllegalArgumentException("callback must not be null");
-                }
-                final String classname = message.getString("classname");
-                final String filename = message.getString("filename");
-                final EventDispatcherImpl dispatcher;
-                try {
-                    dispatcher = registerNewInstance(classname, filename);
-                    callback.sendSuccess(dispatcher.guid);
-                } catch (final Exception e) {
-                    Log.e(LOGTAG, "Unable to load dex successfully", e);
-                    callback.sendError(e.toString());
-                }
-            }
-            break;
-
-            case MESSAGE_UNLOAD: {
-                if (callback == null) {
-                    throw new IllegalArgumentException("callback must not be null");
-                }
-                final String guid = message.getString("guid");
-                final EventDispatcherImpl dispatcher = mGUIDToDispatcherMap.remove(guid);
-                if (dispatcher == null) {
-                    Log.w(LOGTAG, "Attempting to unload addon with unknown " +
-                                  "associated dispatcher; ignoring.");
-                    callback.sendSuccess(false);
-                } else {
-                    dispatcher.unregisterAllEventListeners();
-                    callback.sendSuccess(true);
-                }
-            }
-            break;
-        }
-    }
-
-    /**
-     * An event dispatcher is tied to a single Java Addon instance.  It serves to prefix all
-     * messages with its unique GUID.
-     * <p/>
-     * Curiously, the dispatcher does not hold a direct reference to its add-on instance.  It will
-     * likely hold indirect instances through its wrapping map, since the instance will probably
-     * register event listeners that hold a reference to itself.  When these listeners are
-     * unregistered, any link will be broken, allowing the instances to be garbage collected.
-     */
-    private class EventDispatcherImpl implements JavaAddonInterfaceV1.EventDispatcher {
-        private final String guid;
-        private final String dexFileName;
-
-        // Protected by synchronized (this).
-        private final Map<JavaAddonInterfaceV1.EventListener, Pair<BundleEventListener, String[]>>
-                mListenerToWrapperMap = new IdentityHashMap<>();
-
-        public EventDispatcherImpl(String guid, String dexFileName) {
-            this.guid = guid;
-            this.dexFileName = dexFileName;
-        }
-
-        protected class ListenerWrapper implements BundleEventListener {
-            private final JavaAddonInterfaceV1.EventListener listener;
-
-            public ListenerWrapper(JavaAddonInterfaceV1.EventListener listener) {
-                this.listener = listener;
-            }
-
-            @Override // BundleEventListener
-            public void handleMessage(final String prefixedEvent, final GeckoBundle message,
-                                      final EventCallback callback) {
-                if (!prefixedEvent.startsWith(guid + ":")) {
-                    return;
-                }
-                final String event = prefixedEvent.substring(guid.length() + 1); // Skip "guid:".
-                final JavaAddonInterfaceV1.EventCallback callbackAdapter;
-                if (callback == null) {
-                    callbackAdapter = null;
-                } else {
-                    callbackAdapter = new JavaAddonInterfaceV1.EventCallback() {
-                        private void invokeCallback(final boolean success, final Object response) {
-                            final GeckoBundle bundle;
-                            try {
-                                final JSONObject wrapper = new JSONObject();
-                                wrapper.put("response", response);
-                                bundle = GeckoBundle.fromJSONObject(wrapper);
-                            } catch (final JSONException e) {
-                                Log.e(LOGTAG, "Unsupported response for message [" + event + "]", e);
-                                return;
-                            }
-                            if (success) {
-                                callback.sendSuccess(bundle);
-                            } else {
-                                callback.sendError(bundle);
-                            }
-                        }
-
-                        @Override
-                        public void sendSuccess(Object response) {
-                            invokeCallback(/* success */ true, response);
-                        }
-
-                        @Override
-                        public void sendError(Object response) {
-                            invokeCallback(/* success */ false, response);
-                        }
-                    };
-                }
-                final JSONObject json;
-                try {
-                    json = message.toJSONObject();
-                } catch (final JSONException e) {
-                    Log.e(LOGTAG, "Exception handling message [" + prefixedEvent + "]", e);
-                    return;
-                }
-                listener.handleMessage(mApplicationContext, event, json, callbackAdapter);
-            }
-        }
-
-        @Override
-        public synchronized void registerEventListener(
-                final JavaAddonInterfaceV1.EventListener listener, String... events) {
-            if (mListenerToWrapperMap.containsKey(listener)) {
-                Log.e(LOGTAG, "Attempting to register listener which is already registered; ignoring.");
-                return;
-            }
-
-            final BundleEventListener listenerWrapper = new ListenerWrapper(listener);
-
-            final String[] prefixedEvents = new String[events.length];
-            for (int i = 0; i < events.length; i++) {
-                prefixedEvents[i] = this.guid + ":" + events[i];
-            }
-            mDispatcher.registerGeckoThreadListener(listenerWrapper, prefixedEvents);
-            mListenerToWrapperMap.put(listener, new Pair<>(listenerWrapper, prefixedEvents));
-        }
-
-        @Override
-        public synchronized void unregisterEventListener(
-                final JavaAddonInterfaceV1.EventListener listener) {
-            final Pair<BundleEventListener, String[]> pair = mListenerToWrapperMap.remove(listener);
-            if (pair == null) {
-                Log.e(LOGTAG, "Attempting to unregister listener which is not registered; ignoring.");
-                return;
-            }
-            mDispatcher.unregisterGeckoThreadListener(pair.first, pair.second);
-        }
-
-        protected synchronized void unregisterAllEventListeners() {
-            // Unregister everything, then forget everything.
-            for (Pair<BundleEventListener, String[]> pair : mListenerToWrapperMap.values()) {
-                 mDispatcher.unregisterGeckoThreadListener(pair.first, pair.second);
-            }
-            mListenerToWrapperMap.clear();
-        }
-
-        @Override
-        public void sendRequestToGecko(final String event, final JSONObject message,
-                                       final JavaAddonInterfaceV1.RequestCallback callback) {
-            final String prefixedEvent = guid + ":" + event;
-            final GeckoBundle data;
-            try {
-                data = GeckoBundle.fromJSONObject(message);
-            } catch (final JSONException e) {
-                Log.e(LOGTAG, "Cannot convert message", e);
-                return;
-            }
-
-            final EventCallback cb;
-            if (callback == null) {
-                cb = null;
-            } else {
-                cb = new EventCallback() {
-                    @Override
-                    public void sendSuccess(final Object response) {
-                        if (!(response instanceof GeckoBundle)) {
-                            Log.e(LOGTAG, "Response to request [" + event + "] must be an object");
-                            return;
-                        }
-                        try {
-                            final JSONObject json = ((GeckoBundle) response).toJSONObject();
-                            callback.onResponse(GeckoAppShell.getApplicationContext(), json);
-                        } catch (final Exception e) {
-                            // No way to report failure.
-                            Log.e(LOGTAG, "Exception handling response to request [" +
-                                          event + "]", e);
-                        }
-                    }
-
-                    @Override
-                    public void sendError(final Object response) {
-                        Log.e(LOGTAG, "Exception handling response to request [" +
-                                      event + "]: " + response);
-                    }
-                };
-            }
-
-            mDispatcher.dispatch(prefixedEvent, data, cb);
-        }
-    }
-}
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -41,19 +41,16 @@ with Files('java/org/mozilla/gecko/first
     BUG_COMPONENT = ('Firefox for Android', 'First Run')
 
 with Files('java/org/mozilla/gecko/home/**'):
     BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen')
 
 with Files('java/org/mozilla/gecko/icons/**'):
     BUG_COMPONENT = ('Firefox for Android', 'Favicon Handling')
 
-with Files('java/org/mozilla/gecko/javaaddons/**'):
-    BUG_COMPONENT = ('Firefox for Android', 'General')
-
 with Files('java/org/mozilla/gecko/mdns/**'):
     BUG_COMPONENT = ('Firefox for Android', 'General')
 
 with Files('java/org/mozilla/gecko/media/**'):
     BUG_COMPONENT = ('Firefox for Android', 'Audio/Video')
 
 with Files('java/org/mozilla/gecko/mdns/**'):
     BUG_COMPONENT = ('Firefox for Android', 'Settings and Preferences')
@@ -719,18 +716,16 @@ gbjar.sources += ['java/org/mozilla/geck
     'icons/processing/DiskProcessor.java',
     'icons/processing/MemoryProcessor.java',
     'icons/processing/Processor.java',
     'icons/processing/ResizingProcessor.java',
     'icons/storage/DiskStorage.java',
     'icons/storage/FailureCache.java',
     'icons/storage/MemoryStorage.java',
     'IntentHelper.java',
-    'javaaddons/JavaAddonManager.java',
-    'javaaddons/JavaAddonManagerV1.java',
     'LauncherActivity.java',
     'lwt/LightweightTheme.java',
     'lwt/LightweightThemeDrawable.java',
     'mdns/MulticastDNSManager.java',
     'media/AudioFocusAgent.java',
     'media/MediaControlService.java',
     'media/VideoPlayer.java',
     'MediaCastingBar.java',
@@ -1005,17 +1000,16 @@ if max_sdk_version >= 11:
         'tabs/TabStripAdapter.java',
         'tabs/TabStripDividerItem.java',
         'tabs/TabStripItemAnimator.java',
         'tabs/TabStripItemView.java',
         'tabs/TabStripView.java'
     ]]
 
 gbjar.extra_jars += [
-    OBJDIR + '/../javaaddons/javaaddons-1.0.jar',
     'gecko-R.jar',
     'gecko-mozglue.jar',
     'gecko-thirdparty.jar',
     'gecko-util.jar',
     'gecko-view.jar',
     'sync-thirdparty.jar',
     'services.jar',
 ]
--- a/mobile/android/config/proguard/proguard.cfg
+++ b/mobile/android/config/proguard/proguard.cfg
@@ -148,25 +148,16 @@
     @org.mozilla.gecko.annotation.RobocopTarget <methods>;
 }
 -keepclasseswithmembers class * {
     @org.mozilla.gecko.annotation.RobocopTarget <fields>;
 }
 
 -keep class **.R$*
 
-# Keep all interfaces that might be dynamically required by Java Addons.
--keep class org.mozilla.javaaddons.* {
-    *;
-}
-
--keep class org.mozilla.javaaddons.*$* {
-    *;
-}
-
 # Disable obfuscation because it makes exception stack traces more difficult to read.
 -dontobfuscate
 
 # Suppress warnings about missing descriptor classes.
 #-dontnote **,!ch.boye.**,!org.mozilla.gecko.sync.**
 
 -include "play-services-keeps.cfg"
 
deleted file mode 100644
--- a/mobile/android/javaaddons/Makefile.in
+++ /dev/null
@@ -1,9 +0,0 @@
-# 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/.
-
-include $(topsrcdir)/config/rules.mk
-
-include $(topsrcdir)/config/android-common.mk
-
-libs:: javaaddons-1.0.jar
deleted file mode 100644
--- a/mobile/android/javaaddons/java/org/mozilla/javaaddons/JavaAddonInterfaceV1.java
+++ /dev/null
@@ -1,51 +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.javaaddons;
-
-import android.content.Context;
-import org.json.JSONObject;
-
-public interface JavaAddonInterfaceV1 {
-    /**
-     * Callback interface for Gecko requests.
-     * <p/>
-     * For each instance of EventCallback, exactly one of sendResponse, sendError, must be called to prevent observer leaks.
-     * If more than one send* method is called, or if a single send method is called multiple times, an
-     * {@link IllegalStateException} will be thrown.
-     */
-    interface EventCallback {
-        /**
-         * Sends a success response with the given data.
-         *
-         * @param response The response data to send to Gecko. Can be any of the types accepted by
-         *                 JSONObject#put(String, Object).
-         */
-        public void sendSuccess(Object response);
-
-        /**
-         * Sends an error response with the given data.
-         *
-         * @param response The response data to send to Gecko. Can be any of the types accepted by
-         *                 JSONObject#put(String, Object).
-         */
-        public void sendError(Object response);
-    }
-
-    interface EventDispatcher {
-        void registerEventListener(EventListener listener, String... events);
-        void unregisterEventListener(EventListener listener);
-
-        void sendRequestToGecko(String event, JSONObject message, RequestCallback callback);
-    }
-
-    interface EventListener {
-        public void handleMessage(final Context context, final String event, final JSONObject message, final EventCallback callback);
-    }
-
-    interface RequestCallback {
-        void onResponse(final Context context, JSONObject jsonObject);
-    }
-}
deleted file mode 100644
--- a/mobile/android/javaaddons/moz.build
+++ /dev/null
@@ -1,14 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-with Files('**'):
-    BUG_COMPONENT = ('Firefox for Android', 'General')
-
-jar = add_java_jar('javaaddons-1.0')
-jar.sources = [
-    'java/org/mozilla/javaaddons/JavaAddonInterfaceV1.java',
-]
-jar.javac_flags += ['-Xlint:all']
deleted file mode 100644
--- a/mobile/android/modules/JavaAddonManager.jsm
+++ /dev/null
@@ -1,128 +0,0 @@
-// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
-/* 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/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["JavaAddonManager"];
-
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components; /*global Components */
-
-Cu.import("resource://gre/modules/Messaging.jsm"); /*global Messaging */
-Cu.import("resource://gre/modules/Services.jsm"); /*global Services */
-
-function resolveGeckoURI(uri) {
-  if (!uri) {
-    throw new Error("Can't resolve an empty uri");
-  }
-  if (uri.startsWith("chrome://")) {
-    let registry = Cc['@mozilla.org/chrome/chrome-registry;1'].getService(Ci["nsIChromeRegistry"]);
-    return registry.convertChromeURL(Services.io.newURI(uri)).spec;
-  } else if (uri.startsWith("resource://")) {
-    let handler = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
-    return handler.resolveURI(Services.io.newURI(uri));
-  }
-  return uri;
-}
-
-/**
- * A promise-based API
- */
-var JavaAddonManager = Object.freeze({
-  classInstanceFromFile: function(classname, filename) {
-    if (!classname) {
-      throw new Error("classname cannot be null");
-    }
-    if (!filename) {
-      throw new Error("filename cannot be null");
-    }
-    return EventDispatcher.instance.sendRequestForResult({
-      type: "JavaAddonManagerV1:Load",
-      classname: classname,
-      filename: resolveGeckoURI(filename)
-    })
-      .then((guid) => {
-        if (!guid) {
-          throw new Error("Internal error: guid should not be null");
-        }
-        return new JavaAddonV1({classname: classname, guid: guid});
-      });
-  }
-});
-
-function JavaAddonV1(options = {}) {
-  if (!(this instanceof JavaAddonV1)) {
-    return new JavaAddonV1(options);
-  }
-  if (!options.classname) {
-    throw new Error("options.classname cannot be null");
-  }
-  if (!options.guid) {
-    throw new Error("options.guid cannot be null");
-  }
-  this._classname = options.classname;
-  this._guid = options.guid;
-  this._loaded = true;
-
-  this._listeners = {
-    list: {},
-    onEvent: function(event, data, callback) {
-      let listener = this.list[event];
-      if (listener) {
-        let ret = listener(data);
-        callback && callback.onSuccess(ret);
-      } else {
-        callback && callback.onError("No listener");
-      }
-    },
-  };
-}
-
-JavaAddonV1.prototype = Object.freeze({
-  unload: function() {
-    if (!this._loaded) {
-      return;
-    }
-
-    EventDispatcher.instance.sendRequestForResult({
-      type: "JavaAddonManagerV1:Unload",
-      guid: this._guid
-    })
-      .then(() => {
-        this._loaded = false;
-        for (let event in this._listeners.list) {
-          // If we use this.removeListener, we prefix twice.
-          EventDispatcher.instance.unregisterListener(this._listeners, event);
-        }
-        this._listeners.list = {};
-      });
-  },
-
-  _prefix: function(message) {
-    let newMessage = Cu.cloneInto(message, {}, { cloneFunctions: false });
-    newMessage.type = this._guid + ":" + message.type;
-    return newMessage;
-  },
-
-  sendRequest: function(message) {
-    return EventDispatcher.instance.sendRequest(this._prefix(message));
-  },
-
-  sendRequestForResult: function(message) {
-    return EventDispatcher.instance.sendRequestForResult(this._prefix(message)).then(
-        wrapper => wrapper.response, wrapper => { throw wrapper && wrapper.response; });
-  },
-
-  addListener: function(listener, message) {
-    let prefixedMessage = this._guid + ":" + message;
-    this._listeners.list[prefixedMessage] = listener;
-    return EventDispatcher.instance.registerListener(this._listeners, prefixedMessage);
-  },
-
-  removeListener: function(message) {
-    let prefixedMessage = this._guid + ":" + message;
-    delete this._listeners.list[prefixedMessage];
-    return EventDispatcher.instance.unregisterListener(this._listeners, prefixedMessage);
-  }
-});
--- a/mobile/android/modules/moz.build
+++ b/mobile/android/modules/moz.build
@@ -23,17 +23,16 @@ EXTRA_JS_MODULES += [
     'Accounts.jsm',
     'dbg-browser-actors.js',
     'DelayedInit.jsm',
     'DownloadNotifications.jsm',
     'FxAccountsWebChannel.jsm',
     'HelperApps.jsm',
     'Home.jsm',
     'HomeProvider.jsm',
-    'JavaAddonManager.jsm',
     'JNI.jsm',
     'LightweightThemeConsumer.jsm',
     'MediaPlayerApp.jsm',
     'NetErrorHelper.jsm',
     'Notifications.jsm',
     'PageActions.jsm',
     'Prompt.jsm',
     'RuntimePermissions.jsm',
--- a/mobile/android/moz.build
+++ b/mobile/android/moz.build
@@ -55,17 +55,16 @@ DIRS += [
     '../locales',
     'locales',
 ]
 
 if CONFIG['MOZ_ANDROID_MLS_STUMBLER']:
     DIRS += ['stumbler']
 
 DIRS += [
-    'javaaddons', # Must be built before base.
     'base',
     'chrome',
     'components',
     'extensions',
     'modules',
     'themes/core',
     'app',
     'fonts',
--- a/mobile/android/tests/browser/chrome/chrome.ini
+++ b/mobile/android/tests/browser/chrome/chrome.ini
@@ -27,17 +27,16 @@ support-files =
 skip-if = debug
 [test_debugger_server.html]
 [test_desktop_useragent.html]
 [test_device_search_engine.html]
 [test_get_last_visited.html]
 [test_home_provider.html]
 [test_hidden_select_option.html]
 [test_identity_mode.html]
-[test_java_addons.html]
 [test_jni.html]
 [test_migrate_ui.html]
 [test_network_manager.html]
 [test_offline_page.html]
 skip-if = true # Bug 1241478
 [test_reader_view.html]
 [test_resource_substitutions.html]
 [test_restricted_profiles.html]
deleted file mode 100644
--- a/mobile/android/tests/browser/chrome/test_java_addons.html
+++ /dev/null
@@ -1,119 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1168407
-Migrated from Robocop https://bugzilla.mozilla.org/show_bug.cgi?id=1184186
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for Bug 1168407</title>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-  <script type="application/javascript">
-
-  const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-  Cu.import("resource://gre/modules/JavaAddonManager.jsm"); /*global JavaAddonManager */
-  Cu.import("resource://gre/modules/Promise.jsm"); /*global Promise */
-  Cu.import("resource://gre/modules/Task.jsm"); /*global Task */
-
-  const DEX_FILE = "chrome://roboextender/content/javaaddons-test.apk";
-  const CLASS = "org.mozilla.javaaddons.test.JavaAddonV1";
-
-  const MESSAGE = "JavaAddon:V1";
-
-  add_task(function* testFailureCases() {
-    info("Loading Java Addon from non-existent class.");
-    let gotError1 = yield JavaAddonManager.classInstanceFromFile(CLASS + "GARBAGE", DEX_FILE)
-      .then((result) => false)
-      .catch((error) => true);
-    is(gotError1, true, "got expected error for non-existent class");
-
-    info("Loading Java Addon from non-existent DEX file.");
-    let gotError2 = yield JavaAddonManager.classInstanceFromFile(CLASS, DEX_FILE + "GARBAGE")
-      .then((result) => false)
-      .catch((error) => true);
-    is(gotError2, true, "got expected error for non-existent DEX file");
-  });
-
-  // Make a request to a dynamically loaded Java Addon; wait for a response.
-  // Then expect the add-on to make a request; respond.
-  // Then expect the add-on to make a second request; use it to verify the response to the first request.
-  add_task(function* testJavaAddonV1() {
-    info("Loading Java Addon from: " + DEX_FILE);
-
-    let javaAddon = yield JavaAddonManager.classInstanceFromFile(CLASS, DEX_FILE);
-    isnot(javaAddon, null, "addon is not null");
-    isnot(javaAddon._guid, null, "guid is not null");
-    is(javaAddon._classname, CLASS, "got expected class");
-    is(javaAddon._loaded, true, "addon is loaded");
-
-    let messagePromise = Promise.defer();
-    var count = 0;
-    function listener(data) {
-      info("Got request initiated from Java Addon: " + data + ", " + typeof(data) + ", " + JSON.stringify(data));
-      count += 1;
-      messagePromise.resolve(); // It's okay to resolve before returning: we'll wait on the verification promise no matter what.
-      return {
-        outputStringKey: "inputStringKey=" + data.inputStringKey,
-        outputIntKey: data.inputIntKey - 1
-      };
-    }
-    javaAddon.addListener(listener, "JavaAddon:V1:Request");
-
-    let verifierPromise = Promise.defer();
-    function verifier(data) {
-      info("Got verification request initiated from Java Addon: " + data + ", " + typeof(data) + ", " + JSON.stringify(data));
-      // These values are from the test Java Addon, after being processed by the :Request listener above.
-      is(data.outputStringKey, "inputStringKey=raw", "got expected outputStringKey");
-      is(data.outputIntKey, 2, "got expected outputIntKey");
-      verifierPromise.resolve();
-      return {};
-    }
-    javaAddon.addListener(verifier, "JavaAddon:V1:VerificationRequest");
-
-    let message = {type: MESSAGE, inputStringKey: "test", inputIntKey: 5};
-    info("Sending request to Java Addon: " + JSON.stringify(message));
-    let output = yield javaAddon.sendRequestForResult(message);
-
-    info("Got response from Java Addon: " + output + ", " + typeof(output) + ", " + JSON.stringify(output));
-    is(output.outputStringKey, "inputStringKey=test", "got expected outputStringKey");
-    is(output.outputIntKey, 6, "got expected outputIntKey");
-
-    // We don't worry about timing out: the harness will (very much later)
-    // kill us if we don't see the expected messages.
-
-    info("Waiting for request initiated from Java Addon.");
-    yield messagePromise.promise;
-    is(count, 1, "count is 1");
-
-    info("Waiting for verification request initiated from Java Addon.");
-    yield verifierPromise.promise;
-
-    info("Sending unregistered request to Java Addon: " + JSON.stringify(message));
-    javaAddon.sendRequest(message);
-
-    // Wait for the above request.
-    info("Waiting for unregistered request to finish processing.");
-    yield javaAddon.sendRequestForResult({type: "JavaAddon:V1:Finish"});
-
-    // The JavaAddon should have removed its listener, so we shouldn't get a response and count should stay the same.
-    is(count, 1, "count is still 1");
-  });
-
-  </script>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1168407">Mozilla Bug 1168407</a>
-<br>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1184186">Migrated from Robocop testJavaAddons</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-</pre>
-</body>
-</html>
--- a/mobile/android/tests/browser/robocop/roboextender/Makefile.in
+++ b/mobile/android/tests/browser/robocop/roboextender/Makefile.in
@@ -1,9 +1,6 @@
 #
 # 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/.
 
 TEST_EXTENSIONS_DIR = $(DEPTH)/_tests/testing/mochitest/extensions
-
-tools::
-	-cp $(DEPTH)/mobile/android/tests/javaaddons/javaaddons-test.apk $(TEST_EXTENSIONS_DIR)/roboextender@mozilla.org/base
deleted file mode 100644
--- a/mobile/android/tests/javaaddons/AndroidManifest.xml.in
+++ /dev/null
@@ -1,14 +0,0 @@
-#filter substitution
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="org.mozilla.javaaddons.test"
-    android:versionCode="1"
-    android:versionName="1.0" >
-
-    <uses-sdk android:minSdkVersion="@MOZ_ANDROID_MIN_SDK_VERSION@"
-#ifdef MOZ_ANDROID_MAX_SDK_VERSION
-              android:maxSdkVersion="@MOZ_ANDROID_MAX_SDK_VERSION@"
-#endif
-              android:targetSdkVersion="@ANDROID_TARGET_SDK@"/>
-
-</manifest>
deleted file mode 100644
--- a/mobile/android/tests/javaaddons/Makefile.in
+++ /dev/null
@@ -1,11 +0,0 @@
-# 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/.
-
-ANDROID_MANIFEST_FILE := $(CURDIR)/AndroidManifest.xml
-
-ANDROID_EXTRA_JARS := javaaddons-test.jar
-
-include $(topsrcdir)/config/rules.mk
-
-tools libs:: $(ANDROID_APK_NAME).apk
deleted file mode 100644
--- a/mobile/android/tests/javaaddons/moz.build
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-with Files('**'):
-    BUG_COMPONENT = ('Firefox for Android', 'Testing')
-
-ANDROID_APK_NAME = 'javaaddons-test'
-ANDROID_APK_PACKAGE = 'org.mozilla.javaaddons.test'
-
-jar = add_java_jar('javaaddons-test')
-jar.extra_jars += [
-    TOPOBJDIR + '/mobile/android/javaaddons/javaaddons-1.0.jar',
-]
-jar.javac_flags += ['-Xlint:all']
-jar.sources += [
-    'src/org/mozilla/javaaddons/test/ClassWithNoRecognizedConstructors.java',
-    'src/org/mozilla/javaaddons/test/JavaAddonV0.java',
-    'src/org/mozilla/javaaddons/test/JavaAddonV1.java',
-]
-
-OBJDIR_PP_FILES.mobile.android.tests.javaaddons += [
-    'AndroidManifest.xml.in',
-]
deleted file mode 100644
--- a/mobile/android/tests/javaaddons/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<resources>
-    <string name="app_name">org.mozilla.javaaddons.test</string>
-</resources>
deleted file mode 100644
--- a/mobile/android/tests/javaaddons/src/org/mozilla/javaaddons/test/ClassWithNoRecognizedConstructors.java
+++ /dev/null
@@ -1,11 +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.javaaddons.test;
-
-public class ClassWithNoRecognizedConstructors {
-    public ClassWithNoRecognizedConstructors(int a, String b, boolean c) {
-    }
-}
deleted file mode 100644
--- a/mobile/android/tests/javaaddons/src/org/mozilla/javaaddons/test/JavaAddonV0.java
+++ /dev/null
@@ -1,24 +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.javaaddons.test;
-
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-
-import java.util.Map;
-
-public class JavaAddonV0 implements Handler.Callback {
-    public JavaAddonV0(Map<String, Handler.Callback> callbacks) {
-        callbacks.put("JavaAddon:V0", this);
-    }
-
-    @Override
-    public boolean handleMessage(Message message) {
-        Log.i("JavaAddon", "handleMessage " + message.toString());
-        return true;
-    }
-}
deleted file mode 100644
--- a/mobile/android/tests/javaaddons/src/org/mozilla/javaaddons/test/JavaAddonV1.java
+++ /dev/null
@@ -1,67 +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.javaaddons.test;
-
-import android.content.Context;
-import android.util.Log;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.javaaddons.JavaAddonInterfaceV1.EventCallback;
-import org.mozilla.javaaddons.JavaAddonInterfaceV1.EventDispatcher;
-import org.mozilla.javaaddons.JavaAddonInterfaceV1.EventListener;
-import org.mozilla.javaaddons.JavaAddonInterfaceV1.RequestCallback;
-
-public class JavaAddonV1 implements EventListener, RequestCallback {
-    protected final EventDispatcher mDispatcher;
-
-    public JavaAddonV1(Context context, EventDispatcher dispatcher) {
-        mDispatcher = dispatcher;
-        mDispatcher.registerEventListener(this, "JavaAddon:V1");
-    }
-
-    @Override
-    public void handleMessage(Context context, String event, JSONObject message, EventCallback callback) {
-        Log.i("JavaAddon", "handleMessage: " + event + ", " + message.toString());
-        final JSONObject output = new JSONObject();
-        try {
-            output.put("outputStringKey", "inputStringKey=" + message.getString("inputStringKey"));
-            output.put("outputIntKey", 1 + message.getInt("inputIntKey"));
-        } catch (JSONException e) {
-            // Should never happen; ignore.
-        }
-        // Respond.
-        if (callback != null) {
-            callback.sendSuccess(output);
-        }
-
-        // And send an independent Gecko event.
-        final JSONObject input = new JSONObject();
-        try {
-            input.put("inputStringKey", "raw");
-            input.put("inputIntKey", 3);
-        } catch (JSONException e) {
-            // Should never happen; ignore.
-        }
-        mDispatcher.sendRequestToGecko("JavaAddon:V1:Request", input, this);
-    }
-
-    @Override
-    public void onResponse(Context context, JSONObject jsonObject) {
-        Log.i("JavaAddon", "onResponse: " + jsonObject.toString());
-        // Unregister event listener, so that the JavaScript side can send a test message and
-        // check it is not handled.
-        mDispatcher.unregisterEventListener(this);
-        mDispatcher.sendRequestToGecko("JavaAddon:V1:VerificationRequest", jsonObject, null);
-
-        mDispatcher.registerEventListener(new EventListener() {
-            @Override
-            public void handleMessage(Context context, String event, JSONObject message, EventCallback callback) {
-                mDispatcher.unregisterEventListener(this);
-                callback.sendSuccess(null);
-            }
-        }, "JavaAddon:V1:Finish");
-    }
-}
--- a/mobile/android/tests/moz.build
+++ b/mobile/android/tests/moz.build
@@ -10,13 +10,11 @@ with Files('**'):
 
 if not CONFIG['MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE']:
     TEST_DIRS += [
         'background',
     ]
 
 TEST_DIRS += [
     'browser',
-    'javaaddons', # Must be built before browser/robocop/roboextender.
-                  # This is enforced in config/recurse.mk.
 ]
 
 ANDROID_INSTRUMENTATION_MANIFESTS += ['browser/robocop/robocop.ini']