Index: content/public/android/java/org/chromium/content/browser/LibraryLoader.java |
diff --git a/content/public/android/java/org/chromium/content/browser/LibraryLoader.java b/content/public/android/java/org/chromium/content/browser/LibraryLoader.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ee9b35886111c4164c28b7aa0c64ec1c04c92e12 |
--- /dev/null |
+++ b/content/public/android/java/org/chromium/content/browser/LibraryLoader.java |
@@ -0,0 +1,239 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.content.browser; |
+ |
+import android.os.AsyncTask; |
+import android.os.Handler; |
+import android.text.TextUtils; |
+import android.util.Log; |
+ |
+// This class provides functionality to: |
+// - synchronously load and register the native library. This is used by callers |
+// that can't do anything useful without the native side. |
+// - asynchronously load and register the native library. This is used by callers |
+// that can do more work in the java-side, and let a separate thread do all the |
+// file IO and library loading. |
+public class LibraryLoader { |
+ private static final String TAG = "LibraryLoader"; |
+ |
+ /* TODO(jrg): resolve up and downstream discrepancy; there is no |
+ * upstream libchromeview.so */ |
+ private static String sLibrary = "chromeview"; |
+ |
+ private static boolean sLoaded = false; |
+ |
+ private static boolean sInitialized = false; |
+ |
+ private static AsyncTask<Void, Void, Boolean> sAsyncLoader; |
+ |
+ /** |
+ * Callback for handling loading of the native library. |
+ * |
+ * <p> The callback methods will always be triggered on the UI thread. |
+ */ |
+ public static interface Callback { |
+ /** |
+ * Called when loading the native library is successful. |
+ */ |
+ void onSuccess(); |
+ |
+ /** |
+ * Called when loading the native library fails. |
+ */ |
+ void onFailure(); |
+ } |
+ |
+ /** |
+ * Sets the library name that is to be loaded. This must be called prior to the library being |
+ * loaded the first time. |
+ * |
+ * @param library The name of the library to be loaded (without the lib prefix). |
+ */ |
+ public static void setLibraryToLoad(String library) { |
+ if (TextUtils.equals(sLibrary, library)) return; |
+ |
+ assert !sLoaded : "Setting the library must happen before load is called."; |
+ sLibrary = library; |
+ } |
+ |
+ /** |
+ * This method blocks until the library is fully loaded and initialized; |
+ * must be called on the thread that the native will call its "main" thread. |
+ */ |
+ public static void loadAndInitSync() { |
+ checkThreadUsage(); |
+ if (sInitialized) { |
+ // Already initialized, nothing to do. |
+ return; |
+ } |
+ if (sAsyncLoader != null) { |
+ // Async initialization in progress, wait. |
+ waitForAsyncInitialized(); |
+ return; |
+ } |
+ loadNow(); |
+ initializeOnMainThread(); |
+ } |
+ |
+ /** |
+ * Block until the library is fully initialized. |
+ * Must be called on the thread that the native will call its "main" thread. |
+ */ |
+ private static void waitForAsyncInitialized() { |
+ checkThreadUsage(); |
+ if (sInitialized) { |
+ // Already initialized. |
+ return; |
+ } |
+ synchronized(LibraryLoader.class) { |
+ try { |
+ while (!sLoaded) { |
+ LibraryLoader.class.wait(); |
+ } |
+ // If the UI thread blocked waiting for the task it will already |
+ // have handled the library load completion, so don't duplicate that work here. |
+ } catch (InterruptedException e) { |
+ } |
+ } |
+ initializeOnMainThread(); |
+ } |
+ |
+ /** |
+ * Kicks off an asynchronous library load, and will asynchronously initialize the |
+ * library when that completes. |
+ * Must be called on the thread that the native will call its "main" thread. |
+ */ |
+ public static void loadAndInitAsync(final Callback onLoadedListener) { |
+ checkThreadUsage(); |
+ if (sInitialized) { |
+ // Already initialized, post our Runnable if needed. |
+ if (onLoadedListener != null) { |
+ new Handler().post(new Runnable() { |
+ @Override |
+ public void run() { |
+ onLoadedListener.onSuccess(); |
+ } |
+ }); |
+ } |
+ return; |
+ } |
+ sAsyncLoader = new AsyncTask<Void, Void, Boolean>() { |
+ @Override |
+ public Boolean doInBackground(Void... voids) { |
+ // We're loading the .so in a background thread. Potentially, this |
+ // can break native code that relies on static initializers using |
+ // thread local storage, as the library would normally load in the |
+ // main thread. If do we hit such cases we should remove those static |
+ // initializers, as we chrome has banned them. |
+ // (Worst case, we can go back to just warming up the file in the system |
+ // cache here and do the actual loading in onPostExecute().) |
+ return loadNow(); |
+ } |
+ |
+ @Override |
+ protected void onPostExecute(Boolean result) { |
+ if (result) { |
+ initializeOnMainThread(); |
+ if (onLoadedListener != null) onLoadedListener.onSuccess(); |
+ } else { |
+ if (onLoadedListener != null) onLoadedListener.onFailure(); |
+ } |
+ |
+ } |
+ }; |
+ sAsyncLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
+ } |
+ |
+ /** |
+ * @throws UnsatisfiedLinkError if the library is not yet initialized. |
+ */ |
+ public static void checkIsReady() { |
+ if (!sInitialized) { |
+ throw new UnsatisfiedLinkError(sLibrary + " is not initialized"); |
+ } |
+ } |
+ |
+ /** |
+ * Loads the library and blocks until the load completes. The caller is responsible |
+ * for subsequently calling initialize(). |
+ * May be called on any thread, but should only be called once. Note the thread |
+ * this is called on will be the thread that runs the native code's static initializers. |
+ * See the comment in doInBackground() for more considerations on this. |
+ * |
+ * @return Whether the native library was successfully loaded. |
+ */ |
+ static boolean loadNow() { |
+ assert !sInitialized; |
+ try { |
+ Log.i(TAG, "loading: " + sLibrary); |
+ System.loadLibrary(sLibrary); |
+ Log.i(TAG, "loaded: " + sLibrary); |
+ synchronized(LibraryLoader.class) { |
+ sLoaded = true; |
+ LibraryLoader.class.notifyAll(); |
+ } |
+ } catch (UnsatisfiedLinkError e) { |
+ Log.e(TAG, "error loading: " + sLibrary, e); |
+ return false; |
+ } |
+ return true; |
+ } |
+ |
+ /** |
+ * initializes the library here and now: must be called on the thread that the |
+ * native will call its "main" thread. The library must have previously been |
+ * loaded with loadNow. |
+ * @param initCommandLine The command line arguments that native command line will |
+ * be initialized with. |
+ */ |
+ static void initializeOnMainThread(String[] initCommandLine) { |
+ checkThreadUsage(); |
+ if (sInitialized) { |
+ return; |
+ } |
+ if (!nativeLibraryLoadedOnMainThread(initCommandLine)) { |
+ Log.e(TAG, "error calling nativeLibraryLoadedOnMainThread"); |
+ throw new UnsatisfiedLinkError(); |
+ } |
+ // From this point on, native code is ready to use and checkIsReady() |
+ // shouldn't complain from now on (and in fact, it's used by the |
+ // following calls). |
+ sInitialized = true; |
+ CommandLine.enableNativeProxy(); |
+ TraceEvent.setEnabledToMatchNative(); |
+ } |
+ |
+ static private void initializeOnMainThread() { |
+ checkThreadUsage(); |
+ if (!sInitialized) { |
+ initializeOnMainThread(CommandLine.getJavaSwitchesOrNull()); |
+ } |
+ } |
+ |
+ private LibraryLoader() { |
+ } |
+ |
+ // The public API of this class is meant to be used from a single |
+ // thread. Internally, we may bounce to a separate thread to actually |
+ // load the library. |
+ private static Thread sMyThread; |
+ private static void checkThreadUsage() { |
+ Thread currentThread = java.lang.Thread.currentThread(); |
+ if (sMyThread == null) { |
+ sMyThread = currentThread; |
+ } else { |
+ if (sMyThread != currentThread) { |
+ Log.e(TAG, "Threading violation detected. My thread=" + sMyThread + |
+ " but I'm being accessed from thread=" + currentThread); |
+ assert false; |
+ } |
+ } |
+ } |
+ |
+ // This is the only method that is registered during System.loadLibrary, as it |
+ // happens on a different thread. We then call it on the main thread to register |
+ // everything else. |
+ private static native boolean nativeLibraryLoadedOnMainThread(String[] initCommandLine); |
+} |