1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.base.library_loader;
7 import android.content.Context;
8 import android.os.SystemClock;
9 import android.util.Log;
11 import org.chromium.base.CommandLine;
12 import org.chromium.base.JNINamespace;
13 import org.chromium.base.SysUtils;
14 import org.chromium.base.TraceEvent;
17 * This class provides functionality to load and register the native libraries.
18 * Callers are allowed to separate loading the libraries from initializing them.
19 * This may be an advantage for Android Webview, where the libraries can be loaded
20 * by the zygote process, but then needs per process initialization after the
21 * application processes are forked from the zygote process.
23 * The libraries may be loaded and initialized from any thread. Synchronization
24 * primitives are used to ensure that overlapping requests from different
25 * threads are handled sequentially.
27 * See also base/android/library_loader/library_loader_hooks.cc, which contains
28 * the native counterpart to this class.
30 @JNINamespace("base::android")
31 public class LibraryLoader {
32 private static final String TAG = "LibraryLoader";
34 // Guards all access to the libraries
35 private static final Object sLock = new Object();
37 // One-way switch becomes true when the libraries are loaded.
38 private static boolean sLoaded = false;
40 // One-way switch becomes true when the libraries are initialized (
41 // by calling nativeLibraryLoaded, which forwards to LibraryLoaded(...) in
42 // library_loader_hooks.cc).
43 private static boolean sInitialized = false;
45 // One-way switch becomes true if the system library loading failed,
46 // and the right native library was found and loaded by the hack.
47 // The flag is used to report UMA stats later.
48 private static boolean sNativeLibraryHackWasUsed = false;
51 * The same as ensureInitialized(null, false), should only be called
52 * by non-browser processes.
54 * @throws ProcessInitException
56 public static void ensureInitialized() throws ProcessInitException {
57 ensureInitialized(null, false);
61 * This method blocks until the library is fully loaded and initialized.
63 * @param context The context in which the method is called, the caller
64 * may pass in a null context if it doesn't know in which context it
65 * is running, or it doesn't need to work around the issue
68 * When the context is not null and native library was not extracted
69 * by Android package manager, the LibraryLoader class
70 * will extract the native libraries from APK. This is a hack used to
71 * work around some Sony devices with the following platform bug:
74 * @param shouldDeleteOldWorkaroundLibraries The flag tells whether the method
75 * should delete the old workaround libraries or not.
77 public static void ensureInitialized(
78 Context context, boolean shouldDeleteOldWorkaroundLibraries)
79 throws ProcessInitException {
80 synchronized (sLock) {
82 // Already initialized, nothing to do.
85 loadAlreadyLocked(context, shouldDeleteOldWorkaroundLibraries);
86 initializeAlreadyLocked();
91 * Checks if library is fully loaded and initialized.
93 public static boolean isInitialized() {
94 synchronized (sLock) {
100 * The same as loadNow(null, false), should only be called by
101 * non-browser process.
103 * @throws ProcessInitException
105 public static void loadNow() throws ProcessInitException {
106 loadNow(null, false);
110 * Loads the library and blocks until the load completes. The caller is responsible
111 * for subsequently calling ensureInitialized().
112 * May be called on any thread, but should only be called once. Note the thread
113 * this is called on will be the thread that runs the native code's static initializers.
114 * See the comment in doInBackground() for more considerations on this.
116 * @param context The context the code is running, or null if it doesn't have one.
117 * @param shouldDeleteOldWorkaroundLibraries The flag tells whether the method
118 * should delete the old workaround libraries or not.
120 * @throws ProcessInitException if the native library failed to load.
122 public static void loadNow(Context context, boolean shouldDeleteOldWorkaroundLibraries)
123 throws ProcessInitException {
124 synchronized (sLock) {
125 loadAlreadyLocked(context, shouldDeleteOldWorkaroundLibraries);
130 * initializes the library here and now: must be called on the thread that the
131 * native will call its "main" thread. The library must have previously been
132 * loaded with loadNow.
134 public static void initialize() throws ProcessInitException {
135 synchronized (sLock) {
136 initializeAlreadyLocked();
140 // Invoke System.loadLibrary(...), triggering JNI_OnLoad in native code
141 private static void loadAlreadyLocked(
142 Context context, boolean shouldDeleteOldWorkaroundLibraries)
143 throws ProcessInitException {
146 assert !sInitialized;
148 long startTime = SystemClock.uptimeMillis();
149 boolean useChromiumLinker = Linker.isUsed();
151 if (useChromiumLinker) Linker.prepareLibraryLoad();
153 for (String library : NativeLibraries.LIBRARIES) {
154 if (useChromiumLinker) {
155 if (Linker.isInZipFile()) {
156 String zipfile = context.getApplicationInfo().sourceDir;
157 Log.i(TAG, "Loading " + library + " from within " + zipfile);
158 Linker.loadLibraryInZipFile(zipfile, library);
160 Log.i(TAG, "Loading: " + library);
161 Linker.loadLibrary(library);
165 System.loadLibrary(library);
166 } catch (UnsatisfiedLinkError e) {
168 && LibraryLoaderHelper.tryLoadLibraryUsingWorkaround(context,
170 sNativeLibraryHackWasUsed = true;
177 if (useChromiumLinker) Linker.finishLibraryLoad();
180 && shouldDeleteOldWorkaroundLibraries
181 && !sNativeLibraryHackWasUsed) {
182 LibraryLoaderHelper.deleteWorkaroundLibrariesAsynchronously(
186 long stopTime = SystemClock.uptimeMillis();
187 Log.i(TAG, String.format("Time to load native libraries: %d ms (timestamps %d-%d)",
188 stopTime - startTime,
192 nativeInitCommandLine(CommandLine.getJavaSwitchesOrNull());
193 CommandLine.enableNativeProxy();
197 } catch (UnsatisfiedLinkError e) {
198 throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED, e);
200 // Check that the version of the library we have loaded matches the version we expect
201 Log.i(TAG, String.format(
202 "Expected native library version number \"%s\"," +
203 "actual native library version number \"%s\"",
204 NativeLibraries.VERSION_NUMBER,
205 nativeGetVersionNumber()));
206 if (!NativeLibraries.VERSION_NUMBER.equals(nativeGetVersionNumber())) {
207 throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION);
211 // Invoke base::android::LibraryLoaded in library_loader_hooks.cc
212 private static void initializeAlreadyLocked() throws ProcessInitException {
217 if (!nativeLibraryLoaded()) {
218 Log.e(TAG, "error calling nativeLibraryLoaded");
219 throw new ProcessInitException(LoaderErrors.LOADER_ERROR_FAILED_TO_REGISTER_JNI);
221 // From this point on, native code is ready to use and checkIsReady()
222 // shouldn't complain from now on (and in fact, it's used by the
226 // From now on, keep tracing in sync with native.
227 TraceEvent.registerNativeEnabledObserver();
230 // Called after all native initializations are complete.
231 public static void onNativeInitializationComplete() {
232 // Record histogram for the Chromium linker.
233 if (Linker.isUsed()) {
234 nativeRecordChromiumAndroidLinkerHistogram(Linker.loadAtFixedAddressFailed(),
235 SysUtils.isLowEndDevice());
238 nativeRecordNativeLibraryHack(sNativeLibraryHackWasUsed);
241 private static native void nativeInitCommandLine(String[] initCommandLine);
243 // Only methods needed before or during normal JNI registration are during System.OnLoad.
244 // nativeLibraryLoaded is then called to register everything else. This process is called
245 // "initialization". This method will be mapped (by generated code) to the LibraryLoaded
246 // definition in base/android/library_loader/library_loader_hooks.cc.
248 // Return true on success and false on failure.
249 private static native boolean nativeLibraryLoaded();
251 // Method called to record statistics about the Chromium linker operation,
252 // i.e. whether the library failed to be loaded at a fixed address, and
253 // whether the device is 'low-memory'.
254 private static native void nativeRecordChromiumAndroidLinkerHistogram(
255 boolean loadedAtFixedAddressFailed,
256 boolean isLowMemoryDevice);
258 // Get the version of the native library. This is needed so that we can check we
259 // have the right version before initializing the (rest of the) JNI.
260 private static native String nativeGetVersionNumber();
262 private static native void nativeRecordNativeLibraryHack(boolean usedHack);