// 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.app; import android.text.TextUtils; import android.util.Log; import org.chromium.base.JNINamespace; import org.chromium.base.SysUtils; import org.chromium.content.common.CommandLine; import org.chromium.content.common.ProcessInitException; import org.chromium.content.common.ResultCodes; import org.chromium.content.common.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 content/app/android/library_loader_hooks.cc, which contains * the native counterpart to this class. */ @JNINamespace("content") 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; // List of libraries to load private static String[] libraries; // TODO(cjhopman): Remove this once it's unused. /** * Doesn't do anything. */ @Deprecated public static void setLibraryToLoad(String...libs) { libraries = libs; } /** * This method blocks until the library is fully loaded and initialized. */ public static void ensureInitialized() throws ProcessInitException { synchronized (sLock) { if (sInitialized) { // Already initialized, nothing to do. return; } loadAlreadyLocked(); initializeAlreadyLocked(CommandLine.getJavaSwitchesOrNull()); } } /** * Checks if library is fully loaded and initialized. */ public static boolean isInitialized() { synchronized (sLock) { return sInitialized; } } /** * 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. * * @throws ProcessInitException if the native library failed to load. */ public static void loadNow() throws ProcessInitException { synchronized (sLock) { loadAlreadyLocked(); } } /** * 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 initialize(String[] initCommandLine) throws ProcessInitException { synchronized (sLock) { initializeAlreadyLocked(initCommandLine); } } // Invoke System.loadLibrary(...), triggering JNI_OnLoad in native code private static void loadAlreadyLocked() throws ProcessInitException { try { if (!sLoaded) { assert !sInitialized; long startTime = System.currentTimeMillis(); boolean useContentLinker = Linker.isUsed(); if (useContentLinker) Linker.prepareLibraryLoad(); if (libraries == null || libraries.length == 0) { throw new RuntimeException("No native libraries were specified to load"); } for (String library : libraries) { Log.i(TAG, "Loading: " + library); if (useContentLinker) Linker.loadLibrary(library); else System.loadLibrary(library); } if (useContentLinker) Linker.finishLibraryLoad(); long stopTime = System.currentTimeMillis(); 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(ResultCodes.RESULT_CODE_NATIVE_LIBRARY_LOAD_FAILED, e); } } // Invoke content::LibraryLoaded in library_loader_hooks.cc private static void initializeAlreadyLocked(String[] initCommandLine) throws ProcessInitException { if (sInitialized) { return; } int resultCode = nativeLibraryLoaded(initCommandLine); if (resultCode != 0) { Log.e(TAG, "error calling nativeLibraryLoaded"); throw new ProcessInitException(resultCode); } // 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 content linker. if (Linker.isUsed()) nativeRecordContentAndroidLinkerHistogram(Linker.loadAtFixedAddressFailed(), SysUtils.isLowEndDevice()); } // This is the only method that is registered during System.loadLibrary. We then call it // to register everything else. This process is called "initialization". // This method will be mapped (by generated code) to the LibraryLoaded // definition in content/app/android/library_loader_hooks.cc. // // Return 0 on success, otherwise return the error code from // content/public/common/result_codes.h. private static native int nativeLibraryLoaded(String[] initCommandLine); // Method called to record statistics about the content 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 nativeRecordContentAndroidLinkerHistogram( boolean loadedAtFixedAddressFailed, boolean isLowMemoryDevice); }