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 Java command line is switched to
42 private static boolean sCommandLineSwitched = false;
44 // One-way switch becomes true when the libraries are initialized (
45 // by calling nativeLibraryLoaded, which forwards to LibraryLoaded(...) in
46 // library_loader_hooks.cc).
47 private static boolean sInitialized = false;
49 // One-way switch becomes true if the system library loading failed,
50 // and the right native library was found and loaded by the hack.
51 // The flag is used to report UMA stats later.
52 private static boolean sNativeLibraryHackWasUsed = false;
55 * The same as ensureInitialized(null, false), should only be called
56 * by non-browser processes.
58 * @throws ProcessInitException
60 public static void ensureInitialized() throws ProcessInitException {
61 ensureInitialized(null, false);
65 * This method blocks until the library is fully loaded and initialized.
67 * @param context The context in which the method is called, the caller
68 * may pass in a null context if it doesn't know in which context it
69 * is running, or it doesn't need to work around the issue
72 * When the context is not null and native library was not extracted
73 * by Android package manager, the LibraryLoader class
74 * will extract the native libraries from APK. This is a hack used to
75 * work around some Sony devices with the following platform bug:
78 * @param shouldDeleteOldWorkaroundLibraries The flag tells whether the method
79 * should delete the old workaround libraries or not.
81 public static void ensureInitialized(
82 Context context, boolean shouldDeleteOldWorkaroundLibraries)
83 throws ProcessInitException {
84 synchronized (sLock) {
86 // Already initialized, nothing to do.
89 loadAlreadyLocked(context, shouldDeleteOldWorkaroundLibraries);
90 initializeAlreadyLocked();
95 * Checks if library is fully loaded and initialized.
97 public static boolean isInitialized() {
98 synchronized (sLock) {
104 * The same as loadNow(null, false), should only be called by
105 * non-browser process.
107 * @throws ProcessInitException
109 public static void loadNow() throws ProcessInitException {
110 loadNow(null, false);
114 * Loads the library and blocks until the load completes. The caller is responsible
115 * for subsequently calling ensureInitialized().
116 * May be called on any thread, but should only be called once. Note the thread
117 * this is called on will be the thread that runs the native code's static initializers.
118 * See the comment in doInBackground() for more considerations on this.
120 * @param context The context the code is running, or null if it doesn't have one.
121 * @param shouldDeleteOldWorkaroundLibraries The flag tells whether the method
122 * should delete the old workaround libraries or not.
124 * @throws ProcessInitException if the native library failed to load.
126 public static void loadNow(Context context, boolean shouldDeleteOldWorkaroundLibraries)
127 throws ProcessInitException {
128 synchronized (sLock) {
129 loadAlreadyLocked(context, shouldDeleteOldWorkaroundLibraries);
134 * initializes the library here and now: must be called on the thread that the
135 * native will call its "main" thread. The library must have previously been
136 * loaded with loadNow.
138 public static void initialize() throws ProcessInitException {
139 synchronized (sLock) {
140 initializeAlreadyLocked();
144 // Invoke System.loadLibrary(...), triggering JNI_OnLoad in native code
145 private static void loadAlreadyLocked(
146 Context context, boolean shouldDeleteOldWorkaroundLibraries)
147 throws ProcessInitException {
150 assert !sInitialized;
152 long startTime = SystemClock.uptimeMillis();
153 boolean useChromiumLinker = Linker.isUsed();
155 if (useChromiumLinker) Linker.prepareLibraryLoad();
157 for (String library : NativeLibraries.LIBRARIES) {
158 if (useChromiumLinker) {
159 if (Linker.isInZipFile()) {
160 String zipfile = context.getApplicationInfo().sourceDir;
161 Log.i(TAG, "Loading " + library + " from within " + zipfile);
162 Linker.loadLibraryInZipFile(zipfile, library);
164 Log.i(TAG, "Loading: " + library);
165 Linker.loadLibrary(library);
169 System.loadLibrary(library);
170 } catch (UnsatisfiedLinkError e) {
172 && LibraryLoaderHelper.tryLoadLibraryUsingWorkaround(context,
174 sNativeLibraryHackWasUsed = true;
181 if (useChromiumLinker) Linker.finishLibraryLoad();
184 && shouldDeleteOldWorkaroundLibraries
185 && !sNativeLibraryHackWasUsed) {
186 LibraryLoaderHelper.deleteWorkaroundLibrariesAsynchronously(
190 long stopTime = SystemClock.uptimeMillis();
191 Log.i(TAG, String.format("Time to load native libraries: %d ms (timestamps %d-%d)",
192 stopTime - startTime,
198 } catch (UnsatisfiedLinkError e) {
199 throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED, e);
201 // Check that the version of the library we have loaded matches the version we expect
202 Log.i(TAG, String.format(
203 "Expected native library version number \"%s\"," +
204 "actual native library version number \"%s\"",
205 NativeLibraries.VERSION_NUMBER,
206 nativeGetVersionNumber()));
207 if (!NativeLibraries.VERSION_NUMBER.equals(nativeGetVersionNumber())) {
208 throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION);
212 // The WebView requires the Command Line to be switched over before
213 // initialization is done. This is okay in the WebView's case since the
214 // JNI is already loaded by this point.
215 public static void switchCommandLineForWebView() {
216 synchronized (sLock) {
217 ensureCommandLineSwitchedAlreadyLocked();
221 // Switch the CommandLine over from Java to native if it hasn't already been done.
222 // This must happen after the code is loaded and after JNI is ready (since after the
223 // switch the Java CommandLine will delegate all calls the native CommandLine).
224 private static void ensureCommandLineSwitchedAlreadyLocked() {
226 if (sCommandLineSwitched) {
229 nativeInitCommandLine(CommandLine.getJavaSwitchesOrNull());
230 CommandLine.enableNativeProxy();
231 sCommandLineSwitched = true;
234 // Invoke base::android::LibraryLoaded in library_loader_hooks.cc
235 private static void initializeAlreadyLocked() throws ProcessInitException {
240 // Setup the native command line if necessary.
241 if (!sCommandLineSwitched) {
242 nativeInitCommandLine(CommandLine.getJavaSwitchesOrNull());
245 if (!nativeLibraryLoaded()) {
246 Log.e(TAG, "error calling nativeLibraryLoaded");
247 throw new ProcessInitException(LoaderErrors.LOADER_ERROR_FAILED_TO_REGISTER_JNI);
249 // From this point on, native code is ready to use and checkIsReady()
250 // shouldn't complain from now on (and in fact, it's used by the
254 // The Chrome JNI is registered by now so we can switch the Java
255 // command line over to delegating to native if it's necessary.
256 if (!sCommandLineSwitched) {
257 CommandLine.enableNativeProxy();
258 sCommandLineSwitched = true;
261 // From now on, keep tracing in sync with native.
262 TraceEvent.registerNativeEnabledObserver();
265 // Called after all native initializations are complete.
266 public static void onNativeInitializationComplete() {
267 // Record histogram for the Chromium linker.
268 if (Linker.isUsed()) {
269 nativeRecordChromiumAndroidLinkerHistogram(Linker.loadAtFixedAddressFailed(),
270 SysUtils.isLowEndDevice());
273 nativeRecordNativeLibraryHack(sNativeLibraryHackWasUsed);
276 private static native void nativeInitCommandLine(String[] initCommandLine);
278 // Only methods needed before or during normal JNI registration are during System.OnLoad.
279 // nativeLibraryLoaded is then called to register everything else. This process is called
280 // "initialization". This method will be mapped (by generated code) to the LibraryLoaded
281 // definition in base/android/library_loader/library_loader_hooks.cc.
283 // Return true on success and false on failure.
284 private static native boolean nativeLibraryLoaded();
286 // Method called to record statistics about the Chromium linker operation,
287 // i.e. whether the library failed to be loaded at a fixed address, and
288 // whether the device is 'low-memory'.
289 private static native void nativeRecordChromiumAndroidLinkerHistogram(
290 boolean loadedAtFixedAddressFailed,
291 boolean isLowMemoryDevice);
293 // Get the version of the native library. This is needed so that we can check we
294 // have the right version before initializing the (rest of the) JNI.
295 private static native String nativeGetVersionNumber();
297 private static native void nativeRecordNativeLibraryHack(boolean usedHack);