Upstream version 10.38.220.0
[platform/framework/web/crosswalk.git] / src / base / android / java / src / org / chromium / base / library_loader / LibraryLoader.java
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.
4
5 package org.chromium.base.library_loader;
6
7 import android.content.Context;
8 import android.os.SystemClock;
9 import android.util.Log;
10
11 import org.chromium.base.CommandLine;
12 import org.chromium.base.JNINamespace;
13 import org.chromium.base.SysUtils;
14 import org.chromium.base.TraceEvent;
15
16 /**
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.
22  *
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.
26  *
27  * See also base/android/library_loader/library_loader_hooks.cc, which contains
28  * the native counterpart to this class.
29  */
30 @JNINamespace("base::android")
31 public class LibraryLoader {
32     private static final String TAG = "LibraryLoader";
33
34     // Guards all access to the libraries
35     private static final Object sLock = new Object();
36
37     // One-way switch becomes true when the libraries are loaded.
38     private static boolean sLoaded = false;
39
40     // One-way switch becomes true when the Java command line is switched to
41     // native.
42     private static boolean sCommandLineSwitched = false;
43
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;
48
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;
53
54     /**
55      * The same as ensureInitialized(null, false), should only be called
56      * by non-browser processes.
57      *
58      * @throws ProcessInitException
59      */
60     public static void ensureInitialized() throws ProcessInitException {
61         ensureInitialized(null, false);
62     }
63
64     /**
65      *  This method blocks until the library is fully loaded and initialized.
66      *
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
70      *    http://b/13216167.
71      *
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:
76      *    http://b/13216167.
77      *
78      *  @param shouldDeleteOldWorkaroundLibraries The flag tells whether the method
79      *    should delete the old workaround libraries or not.
80      */
81     public static void ensureInitialized(
82             Context context, boolean shouldDeleteOldWorkaroundLibraries)
83             throws ProcessInitException {
84         synchronized (sLock) {
85             if (sInitialized) {
86                 // Already initialized, nothing to do.
87                 return;
88             }
89             loadAlreadyLocked(context, shouldDeleteOldWorkaroundLibraries);
90             initializeAlreadyLocked();
91         }
92     }
93
94     /**
95      * Checks if library is fully loaded and initialized.
96      */
97     public static boolean isInitialized() {
98         synchronized (sLock) {
99             return sInitialized;
100         }
101     }
102
103     /**
104      * The same as loadNow(null, false), should only be called by
105      * non-browser process.
106      *
107      * @throws ProcessInitException
108      */
109     public static void loadNow() throws ProcessInitException {
110         loadNow(null, false);
111     }
112
113     /**
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.
119      *
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.
123      *
124      * @throws ProcessInitException if the native library failed to load.
125      */
126     public static void loadNow(Context context, boolean shouldDeleteOldWorkaroundLibraries)
127             throws ProcessInitException {
128         synchronized (sLock) {
129             loadAlreadyLocked(context, shouldDeleteOldWorkaroundLibraries);
130         }
131     }
132
133     /**
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.
137      */
138     public static void initialize() throws ProcessInitException {
139         synchronized (sLock) {
140             initializeAlreadyLocked();
141         }
142     }
143
144     // Invoke System.loadLibrary(...), triggering JNI_OnLoad in native code
145     private static void loadAlreadyLocked(
146             Context context, boolean shouldDeleteOldWorkaroundLibraries)
147             throws ProcessInitException {
148         try {
149             if (!sLoaded) {
150                 assert !sInitialized;
151
152                 long startTime = SystemClock.uptimeMillis();
153                 boolean useChromiumLinker = Linker.isUsed();
154
155                 if (useChromiumLinker) Linker.prepareLibraryLoad();
156
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);
163                         } else {
164                             Log.i(TAG, "Loading: " + library);
165                             Linker.loadLibrary(library);
166                         }
167                     } else {
168                         try {
169                             System.loadLibrary(library);
170                         } catch (UnsatisfiedLinkError e) {
171                             if (context != null
172                                 && LibraryLoaderHelper.tryLoadLibraryUsingWorkaround(context,
173                                                                                      library)) {
174                                 sNativeLibraryHackWasUsed = true;
175                             } else {
176                                 throw e;
177                             }
178                         }
179                     }
180                 }
181                 if (useChromiumLinker) Linker.finishLibraryLoad();
182
183                 if (context != null
184                     && shouldDeleteOldWorkaroundLibraries
185                     && !sNativeLibraryHackWasUsed) {
186                     LibraryLoaderHelper.deleteWorkaroundLibrariesAsynchronously(
187                         context);
188                 }
189
190                 long stopTime = SystemClock.uptimeMillis();
191                 Log.i(TAG, String.format("Time to load native libraries: %d ms (timestamps %d-%d)",
192                         stopTime - startTime,
193                         startTime % 10000,
194                         stopTime % 10000));
195
196                 sLoaded = true;
197             }
198         } catch (UnsatisfiedLinkError e) {
199             throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED, e);
200         }
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);
209         }
210     }
211
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();
218         }
219     }
220
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() {
225         assert sLoaded;
226         if (sCommandLineSwitched) {
227             return;
228         }
229         nativeInitCommandLine(CommandLine.getJavaSwitchesOrNull());
230         CommandLine.enableNativeProxy();
231         sCommandLineSwitched = true;
232     }
233
234     // Invoke base::android::LibraryLoaded in library_loader_hooks.cc
235     private static void initializeAlreadyLocked() throws ProcessInitException {
236         if (sInitialized) {
237             return;
238         }
239
240         // Setup the native command line if necessary.
241         if (!sCommandLineSwitched) {
242             nativeInitCommandLine(CommandLine.getJavaSwitchesOrNull());
243         }
244
245         if (!nativeLibraryLoaded()) {
246             Log.e(TAG, "error calling nativeLibraryLoaded");
247             throw new ProcessInitException(LoaderErrors.LOADER_ERROR_FAILED_TO_REGISTER_JNI);
248         }
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
251         // following calls).
252         sInitialized = true;
253
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;
259         }
260
261         // From now on, keep tracing in sync with native.
262         TraceEvent.registerNativeEnabledObserver();
263     }
264
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());
271         }
272
273         nativeRecordNativeLibraryHack(sNativeLibraryHackWasUsed);
274     }
275
276     private static native void nativeInitCommandLine(String[] initCommandLine);
277
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.
282     //
283     // Return true on success and false on failure.
284     private static native boolean nativeLibraryLoaded();
285
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);
292
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();
296
297     private static native void nativeRecordNativeLibraryHack(boolean usedHack);
298 }