root/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. JNINamespace
  2. ensureInitialized
  3. ensureInitialized
  4. isInitialized
  5. loadNow
  6. loadNow
  7. initialize
  8. loadAlreadyLocked
  9. initializeAlreadyLocked
  10. nativeLibraryLoaded
  11. nativeRecordChromiumAndroidLinkerHistogram
  12. nativeGetVersionNumber
  13. nativeRecordNativeLibraryHack

// Copyright 2014 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.base.library_loader;

import android.content.Context;
import android.os.SystemClock;
import android.util.Log;

import org.chromium.base.CommandLine;
import org.chromium.base.JNINamespace;
import org.chromium.base.SysUtils;
import org.chromium.base.TraceEvent;

/**
 * This class provides functionality to load and register the native libraries.
 * Callers are allowed to separate loading the libraries from initializing them.
 * This may be an advantage for Android Webview, where the libraries can be loaded
 * by the zygote process, but then needs per process initialization after the
 * application processes are forked from the zygote process.
 *
 * The libraries may be loaded and initialized from any thread. Synchronization
 * primitives are used to ensure that overlapping requests from different
 * threads are handled sequentially.
 *
 * See also base/android/library_loader/library_loader_hooks.cc, which contains
 * the native counterpart to this class.
 */
@JNINamespace("base::android")
public class LibraryLoader {
    private static final String TAG = "LibraryLoader";

    // Guards all access to the libraries
    private static final Object sLock = new Object();

    // One-way switch becomes true when the libraries are loaded.
    private static boolean sLoaded = false;

    // One-way switch becomes true when the libraries are initialized (
    // by calling nativeLibraryLoaded, which forwards to LibraryLoaded(...) in
    // library_loader_hooks.cc).
    private static boolean sInitialized = false;

    // One-way switch becomes true if the system library loading failed,
    // and the right native library was found and loaded by the hack.
    // The flag is used to report UMA stats later.
    private static boolean sNativeLibraryHackWasUsed = false;

    /**
     * The same as ensureInitialized(null, false), should only be called
     * by non-browser processes.
     *
     * @throws ProcessInitException
     */
    public static void ensureInitialized() throws ProcessInitException {
        ensureInitialized(null, false);
    }

    /**
     *  This method blocks until the library is fully loaded and initialized.
     *
     *  @param context The context in which the method is called, the caller
     *    may pass in a null context if it doesn't know in which context it
     *    is running, or it doesn't need to work around the issue
     *    http://b/13216167.
     *
     *    When the context is not null and native library was not extracted
     *    by Android package manager, the LibraryLoader class
     *    will extract the native libraries from APK. This is a hack used to
     *    work around some Sony devices with the following platform bug:
     *    http://b/13216167.
     *
     *  @param shouldDeleteOldWorkaroundLibraries The flag tells whether the method
     *    should delete the old workaround libraries or not.
     */
    public static void ensureInitialized(
            Context context, boolean shouldDeleteOldWorkaroundLibraries)
            throws ProcessInitException {
        synchronized (sLock) {
            if (sInitialized) {
                // Already initialized, nothing to do.
                return;
            }
            loadAlreadyLocked(context, shouldDeleteOldWorkaroundLibraries);
            initializeAlreadyLocked(CommandLine.getJavaSwitchesOrNull());
        }
    }

    /**
     * Checks if library is fully loaded and initialized.
     */
    public static boolean isInitialized() {
        synchronized (sLock) {
            return sInitialized;
        }
    }

    /**
     * The same as loadNow(null, false), should only be called by
     * non-browser process.
     *
     * @throws ProcessInitException
     */
    public static void loadNow() throws ProcessInitException {
        loadNow(null, false);
    }

    /**
     * Loads the library and blocks until the load completes. The caller is responsible
     * for subsequently calling ensureInitialized().
     * 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.
     *
     * @param context The context the code is running, or null if it doesn't have one.
     * @param shouldDeleteOldWorkaroundLibraries The flag tells whether the method
     *   should delete the old workaround libraries or not.
     *
     * @throws ProcessInitException if the native library failed to load.
     */
    public static void loadNow(Context context, boolean shouldDeleteOldWorkaroundLibraries)
            throws ProcessInitException {
        synchronized (sLock) {
            loadAlreadyLocked(context, shouldDeleteOldWorkaroundLibraries);
        }
    }

    /**
     * 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.
     */
    public static void initialize(String[] initCommandLine) throws ProcessInitException {
        synchronized (sLock) {
            initializeAlreadyLocked(initCommandLine);
        }
    }

    // Invoke System.loadLibrary(...), triggering JNI_OnLoad in native code
    private static void loadAlreadyLocked(
            Context context, boolean shouldDeleteOldWorkaroundLibraries)
            throws ProcessInitException {
        try {
            if (!sLoaded) {
                assert !sInitialized;

                long startTime = SystemClock.uptimeMillis();
                boolean useChromiumLinker = Linker.isUsed();

                if (useChromiumLinker) Linker.prepareLibraryLoad();

                for (String library : NativeLibraries.LIBRARIES) {
                    Log.i(TAG, "Loading: " + library);
                    if (useChromiumLinker) {
                        Linker.loadLibrary(library);
                    } else {
                        try {
                            System.loadLibrary(library);
                        } catch (UnsatisfiedLinkError e) {
                            if (context != null
                                && LibraryLoaderHelper.tryLoadLibraryUsingWorkaround(context,
                                                                                     library)) {
                                sNativeLibraryHackWasUsed = true;
                            } else {
                                throw e;
                            }
                        }
                    }
                }
                if (useChromiumLinker) Linker.finishLibraryLoad();

                if (context != null
                    && shouldDeleteOldWorkaroundLibraries
                    && !sNativeLibraryHackWasUsed) {
                    LibraryLoaderHelper.deleteWorkaroundLibrariesAsynchronously(
                        context);
                }

                long stopTime = SystemClock.uptimeMillis();
                Log.i(TAG, String.format("Time to load native libraries: %d ms (timestamps %d-%d)",
                        stopTime - startTime,
                        startTime % 10000,
                        stopTime % 10000));
                sLoaded = true;
            }
        } catch (UnsatisfiedLinkError e) {
            throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED, e);
        }
        // Check that the version of the library we have loaded matches the version we expect
        Log.i(TAG, String.format(
                "Expected native library version number \"%s\"," +
                        "actual native library version number \"%s\"",
                NativeLibraries.VERSION_NUMBER,
                nativeGetVersionNumber()));
        if (!NativeLibraries.VERSION_NUMBER.equals(nativeGetVersionNumber())) {
            throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION);
        }
    }

    // Invoke base::android::LibraryLoaded in library_loader_hooks.cc
    private static void initializeAlreadyLocked(String[] initCommandLine)
            throws ProcessInitException {
        if (sInitialized) {
            return;
        }
        if (!nativeLibraryLoaded(initCommandLine)) {
            Log.e(TAG, "error calling nativeLibraryLoaded");
            throw new ProcessInitException(LoaderErrors.LOADER_ERROR_FAILED_TO_REGISTER_JNI);
        }
        // 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();
        // Record histogram for the Chromium linker.
        if (Linker.isUsed()) {
            nativeRecordChromiumAndroidLinkerHistogram(Linker.loadAtFixedAddressFailed(),
                    SysUtils.isLowEndDevice());
        }

        nativeRecordNativeLibraryHack(sNativeLibraryHackWasUsed);
    }

    // Only methods needed before or during normal JNI registration are during System.OnLoad.
    // nativeLibraryLoaded is then called to register everything else.  This process is called
    // "initialization".  This method will be mapped (by generated code) to the LibraryLoaded
    // definition in base/android/library_loader/library_loader_hooks.cc.
    //
    // Return true on success and false on failure.
    private static native boolean nativeLibraryLoaded(String[] initCommandLine);

    // Method called to record statistics about the Chromium linker operation,
    // i.e. whether the library failed to be loaded at a fixed address, and
    // whether the device is 'low-memory'.
    private static native void nativeRecordChromiumAndroidLinkerHistogram(
            boolean loadedAtFixedAddressFailed,
            boolean isLowMemoryDevice);

    // Get the version of the native library. This is needed so that we can check we
    // have the right version before initializing the (rest of the) JNI.
    private static native String nativeGetVersionNumber();

    private static native void nativeRecordNativeLibraryHack(boolean usedHack);
}

/* [<][>][^][v][top][bottom][index][help] */