Chromium Code Reviews| 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..f2892c69b871db26fbe6b5f4da53782bb6ce2215 | 
| --- /dev/null | 
| +++ b/content/public/android/java/org/chromium/content/browser/LibraryLoader.java | 
| @@ -0,0 +1,239 @@ | 
| +// Copyright (c) 2011 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 */ | 
| 
 
Yaron
2012/05/09 01:01:42
There may still be a libchromeview.so though. If t
 
John Grabowski
2012/05/09 17:18:15
Perhaps; naming for action at that level is a bit
 
 | 
| + 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); | 
| +} |