diff options
Diffstat (limited to 'mobile/android/base/java/org/mozilla/gecko/javaaddons/JavaAddonManagerV1.java')
-rw-r--r-- | mobile/android/base/java/org/mozilla/gecko/javaaddons/JavaAddonManagerV1.java | 260 |
1 files changed, 0 insertions, 260 deletions
diff --git a/mobile/android/base/java/org/mozilla/gecko/javaaddons/JavaAddonManagerV1.java b/mobile/android/base/java/org/mozilla/gecko/javaaddons/JavaAddonManagerV1.java deleted file mode 100644 index f361773ca..000000000 --- a/mobile/android/base/java/org/mozilla/gecko/javaaddons/JavaAddonManagerV1.java +++ /dev/null @@ -1,260 +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.GeckoAppShell; -import org.mozilla.gecko.sync.Utils; -import org.mozilla.gecko.util.GeckoJarReader; -import org.mozilla.gecko.util.GeckoRequest; -import org.mozilla.gecko.util.NativeEventListener; -import org.mozilla.gecko.util.NativeJSObject; -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 NativeEventListener { - 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 - public synchronized void handleMessage(String event, NativeJSObject message, org.mozilla.gecko.util.EventCallback callback) { - try { - 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 = registerNewInstance(classname, filename); - callback.sendSuccess(dispatcher.guid); - } - 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); - } - dispatcher.unregisterAllEventListeners(); - callback.sendSuccess(true); - } - break; - } - } catch (Exception e) { - Log.e(LOGTAG, "Exception handling message [" + event + "]", e); - if (callback != null) { - callback.sendError("Exception handling message [" + event + "]: " + e.toString()); - } - } - } - - /** - * 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<NativeEventListener, String[]>> mListenerToWrapperMap = new IdentityHashMap<>(); - - public EventDispatcherImpl(String guid, String dexFileName) { - this.guid = guid; - this.dexFileName = dexFileName; - } - - protected class ListenerWrapper implements NativeEventListener { - private final JavaAddonInterfaceV1.EventListener listener; - - public ListenerWrapper(JavaAddonInterfaceV1.EventListener listener) { - this.listener = listener; - } - - @Override - public void handleMessage(String prefixedEvent, NativeJSObject message, final org.mozilla.gecko.util.EventCallback callback) { - if (!prefixedEvent.startsWith(guid + ":")) { - return; - } - final String event = prefixedEvent.substring(guid.length() + 1); // Skip "guid:". - try { - JavaAddonInterfaceV1.EventCallback callbackAdapter = null; - if (callback != null) { - callbackAdapter = new JavaAddonInterfaceV1.EventCallback() { - @Override - public void sendSuccess(Object response) { - callback.sendSuccess(response); - } - - @Override - public void sendError(Object response) { - callback.sendError(response); - } - }; - } - final JSONObject json = new JSONObject(message.toString()); - listener.handleMessage(mApplicationContext, event, json, callbackAdapter); - } catch (Exception e) { - Log.e(LOGTAG, "Exception handling message [" + prefixedEvent + "]", e); - if (callback != null) { - callback.sendError("Got exception handling message [" + prefixedEvent + "]: " + e.toString()); - } - } - } - } - - @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 NativeEventListener 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<NativeEventListener, 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<NativeEventListener, 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; - GeckoAppShell.sendRequestToGecko(new GeckoRequest(prefixedEvent, message) { - @Override - public void onResponse(NativeJSObject nativeJSObject) { - if (callback == null) { - // Nothing to do. - return; - } - try { - final JSONObject json = new JSONObject(nativeJSObject.toString()); - callback.onResponse(GeckoAppShell.getContext(), json); - } catch (JSONException e) { - // No way to report failure. - Log.e(LOGTAG, "Exception handling response to request [" + event + "]:", e); - } - } - }); - } - } -} |