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(CommandLine.getJavaSwitchesOrNull());
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.
133 * @param initCommandLine The command line arguments that native command line will
134 * be initialized with.
136 public static void initialize(String[] initCommandLine) throws ProcessInitException {
137 synchronized (sLock) {
138 initializeAlreadyLocked(initCommandLine);
142 // Invoke System.loadLibrary(...), triggering JNI_OnLoad in native code
143 private static void loadAlreadyLocked(
144 Context context, boolean shouldDeleteOldWorkaroundLibraries)
145 throws ProcessInitException {
148 assert !sInitialized;
150 long startTime = SystemClock.uptimeMillis();
151 boolean useChromiumLinker = Linker.isUsed();
153 if (useChromiumLinker) Linker.prepareLibraryLoad();
155 for (String library : NativeLibraries.LIBRARIES) {
156 Log.i(TAG, "Loading: " + library);
157 if (useChromiumLinker) {
158 Linker.loadLibrary(library);
161 System.loadLibrary(library);
162 } catch (UnsatisfiedLinkError e) {
164 && LibraryLoaderHelper.tryLoadLibraryUsingWorkaround(context,
166 sNativeLibraryHackWasUsed = true;
173 if (useChromiumLinker) Linker.finishLibraryLoad();
176 && shouldDeleteOldWorkaroundLibraries
177 && !sNativeLibraryHackWasUsed) {
178 LibraryLoaderHelper.deleteWorkaroundLibrariesAsynchronously(
182 long stopTime = SystemClock.uptimeMillis();
183 Log.i(TAG, String.format("Time to load native libraries: %d ms (timestamps %d-%d)",
184 stopTime - startTime,
189 } catch (UnsatisfiedLinkError e) {
190 throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED, e);
192 // Check that the version of the library we have loaded matches the version we expect
193 Log.i(TAG, String.format(
194 "Expected native library version number \"%s\"," +
195 "actual native library version number \"%s\"",
196 NativeLibraries.VERSION_NUMBER,
197 nativeGetVersionNumber()));
198 if (!NativeLibraries.VERSION_NUMBER.equals(nativeGetVersionNumber())) {
199 throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION);
203 // Invoke base::android::LibraryLoaded in library_loader_hooks.cc
204 private static void initializeAlreadyLocked(String[] initCommandLine)
205 throws ProcessInitException {
209 if (!nativeLibraryLoaded(initCommandLine)) {
210 Log.e(TAG, "error calling nativeLibraryLoaded");
211 throw new ProcessInitException(LoaderErrors.LOADER_ERROR_FAILED_TO_REGISTER_JNI);
213 // From this point on, native code is ready to use and checkIsReady()
214 // shouldn't complain from now on (and in fact, it's used by the
217 CommandLine.enableNativeProxy();
219 // From now on, keep tracing in sync with native.
220 TraceEvent.registerNativeEnabledObserver();
222 // Record histogram for the Chromium linker.
223 if (Linker.isUsed()) {
224 nativeRecordChromiumAndroidLinkerHistogram(Linker.loadAtFixedAddressFailed(),
225 SysUtils.isLowEndDevice());
228 nativeRecordNativeLibraryHack(sNativeLibraryHackWasUsed);
231 // Only methods needed before or during normal JNI registration are during System.OnLoad.
232 // nativeLibraryLoaded is then called to register everything else. This process is called
233 // "initialization". This method will be mapped (by generated code) to the LibraryLoaded
234 // definition in base/android/library_loader/library_loader_hooks.cc.
236 // Return true on success and false on failure.
237 private static native boolean nativeLibraryLoaded(String[] initCommandLine);
239 // Method called to record statistics about the Chromium linker operation,
240 // i.e. whether the library failed to be loaded at a fixed address, and
241 // whether the device is 'low-memory'.
242 private static native void nativeRecordChromiumAndroidLinkerHistogram(
243 boolean loadedAtFixedAddressFailed,
244 boolean isLowMemoryDevice);
246 // Get the version of the native library. This is needed so that we can check we
247 // have the right version before initializing the (rest of the) JNI.
248 private static native String nativeGetVersionNumber();
250 private static native void nativeRecordNativeLibraryHack(boolean usedHack);